devi@23: #!/usr/bin/perl -w devi@23: devi@23: # devi@23: # (c) Igor Chubin, imchubin@mail.ru, 2004-2005 devi@23: # devi@23: devi@23: use strict; devi@25: use POSIX; devi@23: use Term::VT102; devi@23: use Text::Iconv; devi@23: use Data::Dumper; devi@23: use Time::Local 'timelocal_nocheck'; devi@27: use IO::Socket; devi@23: devi@23: use lib "."; devi@23: use l3config; devi@23: devi@23: devi@23: our @Command_Lines; devi@23: our @Command_Lines_Index; devi@23: our @Diffs; devi@27: our %Sessions; devi@23: devi@23: our %Commands_Stat; # Statistics about commands usage devi@23: our %Files_Stat; # Statistics about commands usage devi@23: devi@25: our %Script_Files; # Информация о позициях в скрипт-файлах, devi@25: # до которых уже выполнен разбор devi@25: # и информация о времени модификации файла devi@25: # $Script_Files{$file}->{size} devi@25: # $Script_Files{$file}->{tell} devi@25: devi@25: our $Killed =0; # В режиме демона -- процесс получил сигнал о завершении devi@23: devi@23: sub init_variables; devi@23: sub main; devi@23: devi@23: sub load_diff_files; devi@23: sub bind_diff; devi@23: sub extract_from_cline; devi@23: sub load_command_lines; devi@23: sub sort_command_lines; devi@23: sub process_command_lines; devi@23: sub print_command_lines; devi@23: sub printq; devi@23: devi@25: sub save_cache_stat; devi@25: sub load_cache_stat; devi@27: sub print_session; devi@25: devi@23: sub load_diff_files devi@23: { devi@23: my @pathes = @_; devi@23: devi@23: for my $path (@pathes) { devi@23: my $template = "*.diff"; devi@23: my @files = <$path/$template>; devi@23: my $i=0; devi@23: for my $file (@files) { devi@23: my %diff; devi@23: devi@23: $diff{"path"}=$path; devi@23: $diff{"uid"}="SET THIS"; devi@23: devi@23: # Сейчас UID определяется из названия каталога devi@23: # откуда берутся diff-файлы devi@23: # Это неправильно devi@23: # devi@23: # ВАРИАНТ: devi@23: # К файлам жураналам должны прилагаться ситемны файлы, devi@23: # мз которых и будет определяться соответствие devi@23: # имён пользователей их uid'ам devi@23: # devi@23: $diff{"uid"} = 0 if $path =~ m@/root/@; devi@23: devi@23: $diff{"bind_to"}=""; devi@23: $diff{"time_range"}=-1; devi@23: devi@23: next if not $file=~m@/(D?[0-9][0-9]?[0-9]?)[^/]*?([0-9]*):([0-9]*):?([0-9]*)@; devi@23: $diff{"day"}=$1 || ""; devi@23: $diff{"hour"}=$2; devi@23: $diff{"min"}=$3; devi@23: $diff{"sec"}=$4 || 0; devi@23: devi@23: $diff{"index"}=$i; devi@23: devi@23: print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n"; devi@23: devi@23: local $/; devi@23: open (F, "$file") devi@23: or return "Can't open file $file ($_[0]) for reading"; devi@23: my $text = ; devi@23: if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i) { devi@23: my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8"); devi@23: $text = $converter->convert($text); devi@23: } devi@23: close(F); devi@23: $diff{"text"}=$text; devi@23: #print "$file loaded ($diff{day})\n"; devi@23: devi@23: push @Diffs, \%diff; devi@23: $i++; devi@23: } devi@23: } devi@23: } devi@23: devi@23: devi@23: sub bind_diff devi@23: { devi@23: # my $path = shift; devi@23: # my $pid = shift; devi@23: # my $day = shift; devi@23: # my $lab = shift; devi@23: devi@23: print "Trying to bind diff...\n"; devi@23: devi@23: my $cl = shift; devi@23: my $hour = $cl->{"hour"}; devi@23: my $min = $cl->{"min"}; devi@23: my $sec = $cl->{"sec"}; devi@23: devi@23: my $min_dt = 10000; devi@23: devi@23: for my $diff (@Diffs) { devi@23: # Check here date, time and user devi@23: next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"})); devi@23: #next if (!$diff->{"uid"} && $cl->{"euid"} != $diff->{"uid"}); devi@23: devi@23: my $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec); devi@23: if ($dt >0 && $dt < $min_dt && ($diff->{"time_range"} <0 || $dt < $diff->{"time_range"})) { devi@23: print "Approppriate diff found: dt=$dt\n"; devi@23: if ($diff->{"bind_to"}) { devi@23: undef $diff->{"bind_to"}->{"diff"}; devi@23: }; devi@23: $diff->{"time_range"}=$dt; devi@23: $diff->{"bind_to"}=$cl; devi@23: devi@23: $cl->{"diff"} = $diff->{"index"}; devi@23: $min_dt = $dt; devi@23: } devi@23: devi@23: } devi@23: } devi@23: devi@23: devi@23: sub extract_from_cline devi@23: # Разобрать командную строку $_[1] и возвратить хэш, содержащий devi@23: # номер первого появление команды в строке: devi@23: # команда => первая позиция devi@23: { devi@23: my $what = $_[0]; devi@23: my $cline = $_[1]; devi@23: my @lists = split /\;/, $cline; devi@23: devi@23: devi@23: my @commands = (); devi@23: for my $list (@lists) { devi@23: push @commands, split /\|/, $list; devi@23: } devi@23: devi@23: my %commands; devi@23: my %files; devi@23: my $i=0; devi@23: for my $command (@commands) { devi@23: $command =~ /\s*(\S+)\s*(.*)/; devi@23: if ($1 && $1 eq "sudo" ) { devi@23: $commands{"$1"}=$i++; devi@23: $command =~ s/\s*sudo\s+//; devi@23: } devi@23: $command =~ /\s*(\S+)\s*(.*)/; devi@23: if ($1 && !defined $commands{"$1"}) { devi@23: $commands{"$1"}=$i++; devi@23: }; devi@23: if ($2) { devi@23: my $args = $2; devi@23: my @args = split (/\s+/, $args); devi@23: for my $a (@args) { devi@23: $files{"$a"}=$i++ devi@23: if !defined $files{"$a"}; devi@23: }; devi@23: devi@23: devi@23: } devi@23: } devi@23: devi@23: if ($what eq "commands") { devi@23: return %commands; devi@23: } else { devi@23: return %files; devi@23: } devi@23: devi@23: } devi@23: devi@23: sub load_command_lines devi@23: { devi@23: my $lab_scripts_path = $_[0]; devi@23: my $lab_scripts_mask = $_[1]; devi@23: devi@23: my $cline_re_base = qq' devi@25: ( devi@25: (?:\\^?([0-9]*C?)) # exitcode devi@23: (?:_([0-9]+)_)? # uid devi@23: (?:_([0-9]+)_) # pid devi@23: (...?) # day devi@23: (.?.?) # lab devi@23: \\s # space separator devi@23: ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]) # time devi@23: .\\[50D.\\[K # killing symbols devi@23: (.*?([\$\#]\\s?)) # prompt devi@23: (.*) # command line devi@25: ) devi@23: '; devi@23: #my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x; devi@23: #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x; devi@23: my $cline_re = qr/$cline_re_base/sx; devi@23: my $cline_re1 = qr/$cline_re_base\x0D/sx; devi@23: my $cline_re2 = qr/$cline_re_base$/sx; devi@23: devi@23: my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"}, devi@23: 'rows' => $Config{"terminal_height"}); devi@23: my $cline_vt = Term::VT102->new ('cols' => $Config{"terminal_width"}, devi@23: 'rows' => $Config{"terminal_height"}); devi@23: devi@23: my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8") devi@23: if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i); devi@23: devi@23: print "Loading lm-scripts...\n" if $Config{"verbose"} =~ /y/; devi@23: devi@23: my $file; devi@23: my $skip_info; devi@23: devi@23: my $commandlines_loaded =0; devi@23: my $commandlines_processed =0; devi@23: devi@27: my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>; devi@23: for $file (@lab_scripts){ devi@25: devi@25: # Пропускаем файл, если он не изменялся со времени нашего предудущего прохода devi@25: my $size = (stat($file))[7]; devi@25: next if ($Script_Files{$file} && $Script_Files{$file}->{size} && $Script_Files{$file}->{size} >= $size); devi@23: devi@27: devi@27: my $local_session_id; devi@27: # Начальное значение идентификатора текущего сеанса определяем из имени скрипта devi@27: # Впоследствии оно может быть уточнено devi@27: $file =~ /.*\/(.*)\.script$/; devi@27: $local_session_id = $1; devi@27: devi@27: #Если файл только что появился, devi@27: #пытаемся найти и загрузить информацию о соответствующей ему сессии devi@27: if (!$Script_Files{$file}) { devi@27: my $session_file = $file; devi@27: $session_file =~ s/\.script/.info/; devi@27: if (open(SESSION, $session_file)) { devi@27: local $/; devi@27: my $data = ; devi@27: close(SESSION); devi@27: devi@27: for my $session_data ($data =~ m@(.*?)@sg) { devi@27: my %session; devi@27: while ($session_data =~ m@<([^>]*?)>(.*?)@sg) { devi@27: $session{$1} = $2; devi@27: } devi@27: $local_session_id = $session{"local_session_id"} if $session{"local_session_id"}; devi@27: $Sessions{$session_id}=\%session; devi@27: } devi@27: devi@27: #Загруженную информацию сразу же отправляем в поток devi@27: print_session($Config{cache}, $local_session_id); devi@27: } devi@27: } devi@27: devi@23: open (FILE, "$file"); devi@23: binmode FILE; devi@25: devi@25: # Переходим к тому месту, где мы окончили разбор devi@25: seek (FILE, $Script_Files{$file}->{tell}, 0) if $Script_Files{$file}->{tell}; devi@25: $Script_Files{$file}->{size} = $size; devi@25: $Script_Files{$file}->{tell} = 0 unless $Script_Files{$file}->{tell}; devi@25: devi@25: devi@23: $file =~ m@.*/(.*?)-.*@; devi@23: devi@23: my $tty = $1; devi@23: my $first_pass = 1; devi@23: my %cl; devi@23: my $last_output_length=0; devi@23: while () { devi@25: devi@23: $commandlines_processed++; devi@23: # time devi@23: devi@23: if (/[0-9][0-9]:[0-9][0-9]:[0-9][0-9].\[[0-9][0-9]D.\[K/ && m/$cline_re/) { devi@23: s/.*\x0d(?!\x0a)//; devi@23: # print "!!!",$_,"!!!\n"; devi@23: # next; devi@23: # while (m/$cline_re1/gs) { devi@23: # } devi@23: m/$cline_re2/gs; devi@23: devi@23: $commandlines_loaded++; devi@23: $last_output_length=0; devi@23: devi@23: # Previous command devi@23: my %last_cl = %cl; devi@25: my $err = $2 || ""; devi@23: devi@23: devi@23: =cut devi@23: devi@27: Атрибуты cline devi@27: Список полей, характеризующих командную строку devi@23: devi@23: uid devi@23: Идентификатор пользователя devi@23: devi@23: tty devi@23: Идентификатор терминала, на котором была вызвана команда devi@23: devi@23: pid devi@23: PID-процесса командного интерпретатора, devi@23: в котором была вызвана команда devi@23: devi@23: lab devi@23: лабораторная работа, к которой относится команда. devi@23: Идентификатор текущей лабораторной работы devi@23: хранится в файле ~/.labmaker/lab devi@23: devi@23: pwd (!) devi@23: текущий каталог, из которого была вызвана команда devi@23: devi@23: day devi@23: время вызова, день devi@23: В действительности здесь хранится не время вызова команды, devi@23: а с момента появления приглашения командного интерпретатора devi@23: для ввода команды devi@23: devi@23: devi@23: hour devi@23: время вызова, час devi@23: devi@23: min devi@23: время вызова, минута devi@23: devi@23: sec devi@23: время вызова, секунда devi@23: devi@23: time (!) devi@23: время вызова команды в Unix-формате. devi@23: Предпочтительнее использовать этот формат чем hour:min:sec, devi@23: использовавшийся в Labmaker devi@23: devi@23: fullprompt devi@23: Приглашение командной строки devi@23: devi@23: prompt devi@23: Сокращённое приглашение командной строки devi@23: devi@23: cline devi@23: Командная строка devi@23: devi@23: output devi@23: Результат выполнения команды devi@23: devi@23: diff devi@23: Указатель на ассоциированный с командой diff devi@23: devi@23: note (!) devi@23: Текстовый комментарий к команде. devi@23: Может генерироваться из самого лога с помощью команд devi@23: #^ Комментарий devi@27: #= Комментарий devi@23: #v Комментарий devi@23: в том случае, если для комментирования достаточно одной строки, devi@23: или с помощью команд devi@23: cat > /dev/null #^ Заголовок devi@23: Текст devi@23: ^D devi@23: в том случае, если комментарий развёрнутый. devi@23: В последнем случае комментарий может содержать devi@23: заголовок, абзацы и несложное форматирование. devi@23: devi@27: Символы ^, v или = после знака комментария # обозначает, devi@23: к какой команде относится комментарий: devi@27: к предыдущей (^), последующей (v) devi@27: или это общий комментарий по тексту, не относящийся непосредственно devi@27: ни к одной из них (=) devi@23: devi@23: err devi@23: Код завершения командной строки devi@23: devi@23: histnum (!) devi@23: Номер команды в истории командного интерпретатора devi@23: devi@23: status (!) devi@23: Является ли данная команда вызванной (r), запомненной (s) devi@23: или это подсказка completion (c). devi@23: devi@23: Команды, которые были вызваны и обработаны интерпретатором devi@23: имеют состояние "r". К таким командам относится большинство devi@23: команд вводимых в интерпретатор. devi@23: devi@23: Если команда набрана, но вызывать её по какой-либо причине devi@23: не хочется (например, команда может быть не полной, вредоносной devi@23: или просто бессмысленной в текущих условиях), devi@23: её можно сбросить с помощью комбинации клавиш Ctrl-C devi@23: (не путайте с прерыванием работающей команды! здесь она даже devi@23: не запускается!). devi@23: В таком случае она не выполняется, но попадает в журнал devi@23: со статусом "s". devi@23: devi@23: Если команда появилась в журнале благодаря автопроолжению devi@23: -- когда было показано несколько вариантов -- devi@23: она имеет статус "c". devi@23: devi@23: euid devi@23: Идентификатор пользователя от имени которого будет devi@23: выполняться команда. devi@23: Может отличаться от реального uid в том случае, devi@23: если вызывается с помощью sudo devi@23: devi@23: devi@23: version (!) devi@23: Версия lilalo-prompt использовавшаяся при записи devi@23: команды. devi@23: devi@23: 0 - версия использовавшая в labmaker. devi@23: Отсутствует информация о текущем каталоге и номере в истории. devi@23: Информация о версии также не указана в приглашении. devi@23: devi@23: devi@23: 1 - версия использующаяся в lilalo devi@23: devi@27: raw_file devi@23: Имя файла, в котором находится бинарное представление журнала. devi@23: Может содержать ключевое слово HERE, devi@23: обозначающее что бинарное представление хранится devi@23: непосредственно в базе данных в атрибуте raw_data devi@23: devi@27: raw_start devi@23: Начало блока командной строки в файле бинарного представления devi@23: devi@27: raw_output_start devi@25: Начало блока вывода devi@25: devi@27: raw_end devi@23: Конец блока командной строки в файле бинарного представления devi@23: devi@27: raw_cline devi@25: Необработанная командная строка (без приглашения) в бинарном виде devi@23: devi@23: raw_data (*) devi@23: Бинарное представление команды и результатов её выполнения devi@23: devi@23: devi@23: devi@23: devi@23: ТАБЛИЦА SESSION devi@23: devi@23: Информация о сеансах devi@23: devi@27: (см. lm-install) devi@23: devi@23: devi@23: =cut devi@23: devi@27: $cl{"local_session_id"} = $local_session_id; devi@23: # Parse new command devi@25: $cl{"uid"} = $3; devi@23: $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0 devi@25: $cl{"pid"} = $4; devi@25: $cl{"day"} = $5; devi@25: $cl{"lab"} = $6; devi@25: $cl{"hour"} = $7; devi@25: $cl{"min"} = $8; devi@25: $cl{"sec"} = $9; devi@25: $cl{"fullprompt"} = $10; devi@25: $cl{"prompt"} = $11; devi@25: $cl{"raw_cline"} = $12; devi@25: devi@25: { devi@25: use bytes; devi@25: $cl{"raw_start"} = tell (FILE) - length($1); devi@25: $cl{"raw_output_start"} = tell FILE; devi@25: } devi@25: $cl{"raw_file"} = $file; devi@23: devi@23: $cl{"err"} = 0; devi@23: $cl{"output"} = ""; devi@23: $cl{"tty"} = $tty; devi@23: devi@23: $cline_vt->process($cl{"raw_cline"}."\n"); devi@23: $cl{"cline"} = $cline_vt->row_plaintext (1); devi@23: $cl{"cline"} =~ s/\s*$//; devi@23: $cline_vt->reset(); devi@23: devi@23: my %commands = extract_from_cline("commands", $cl{"cline"}); devi@23: $cl{"euid"}=0 if defined $commands{"sudo"}; devi@23: my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands; devi@23: $cl{"last_command"} = $comms[$#comms] || ""; devi@23: devi@23: if ( devi@23: $Config{"suppress_editors"} =~ /^y/i devi@23: && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}}) || devi@23: $Config{"suppress_pagers"} =~ /^y/i devi@23: && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}}) || devi@23: $Config{"suppress_terminal"}=~ /^y/i devi@23: && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}}) devi@23: ) { devi@23: $cl{"suppress_output"} = "1"; devi@23: } devi@23: else { devi@23: $cl{"suppress_output"} = "0"; devi@23: devi@23: } devi@23: $skip_info = 0; devi@23: devi@23: devi@23: print " ",$cl{"last_command"}; devi@23: devi@23: # Processing previous command line devi@23: if ($first_pass) { devi@23: $first_pass = 0; devi@23: next; devi@23: } devi@23: devi@23: # Error code devi@25: $last_cl{"raw_end"} = $cl{"raw_start"}; devi@23: $last_cl{"err"}=$err; devi@23: $last_cl{"err"}=130 if $err eq "^C"; devi@23: devi@23: if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) { devi@23: bind_diff(\%last_cl); devi@23: } devi@23: devi@23: # Output devi@23: if (!$last_cl{"suppress_output"} || $last_cl{"err"}) { devi@23: for (my $i=0; $i<$Config{"terminal_height"}; $i++) { devi@23: my $line= $vt->row_plaintext($i); devi@23: next if !defined ($line) || $line =~ /^\s*$/; devi@23: $line =~ s/\s*$//; devi@23: $last_cl{"output"} .= $line."\n"; devi@23: } devi@23: } devi@23: else { devi@23: $last_cl{"output"}= ""; devi@23: } devi@23: devi@23: $vt->reset(); devi@23: devi@23: devi@23: # Classifying the command line devi@23: devi@23: devi@23: # Save devi@23: if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) { devi@23: # Changing encoding devi@23: for (keys %last_cl) { devi@25: next if /raw/; devi@23: $last_cl{$_} = $converter->convert($last_cl{$_}) devi@23: if ($Config{"encoding"} && devi@23: $Config{"encoding"} !~ /^utf-8$/i); devi@23: } devi@23: push @Command_Lines, \%last_cl; devi@25: devi@25: # Сохранение позиции в файле, до которой выполнен devi@25: # успешный разбор devi@25: $Script_Files{$file}->{tell} = $last_cl{raw_end}; devi@23: } devi@23: next; devi@23: } devi@23: $last_output_length+=length($_); devi@23: #if (!$cl{"suppress_output"} || $last_output_length < 5000) { devi@23: if ($last_output_length < 50000) { devi@23: #print "(",length($_),")" if (length($_) > 2000) ; devi@23: $vt->process("$_"."\n") devi@23: } devi@23: else devi@23: { devi@23: if (!$skip_info) { devi@23: print "($cl{last_command})"; devi@23: $skip_info = 1; devi@23: } devi@23: } devi@23: } devi@23: close(FILE); devi@23: devi@23: } devi@23: if ($Config{"verbose"} =~ /y/) { devi@23: print "...finished." ; devi@23: print "Lines loaded: $commandlines_processed\n"; devi@23: print "Command lines: $commandlines_loaded\n"; devi@23: } devi@23: } devi@23: devi@23: devi@23: devi@23: sub printq devi@23: { devi@23: my $TO = shift; devi@23: my $text = join "", @_; devi@23: $text =~ s/&/&/g; devi@23: $text =~ s//>/g; devi@23: print $TO $text; devi@23: } devi@23: devi@23: devi@23: sub sort_command_lines devi@23: { devi@23: print "Sorting command lines...\n" if $Config{"verbose"} =~ /y/; devi@23: devi@23: # Sort Command_Lines devi@23: # Write Command_Lines to Command_Lines_Index devi@23: devi@23: my @index; devi@23: for (my $i=0;$i<=$#Command_Lines;$i++) { devi@23: $index[$i]=$i; devi@23: } devi@23: devi@23: @Command_Lines_Index = sort { devi@23: $Command_Lines[$index[$a]]->{"day"} cmp $Command_Lines[$index[$b]]->{"day"} || devi@23: $Command_Lines[$index[$a]]->{"hour"} <=> $Command_Lines[$index[$b]]->{"hour"} || devi@23: $Command_Lines[$index[$a]]->{"min"} <=> $Command_Lines[$index[$b]]->{"min"} || devi@23: $Command_Lines[$index[$a]]->{"sec"} <=> $Command_Lines[$index[$b]]->{"sec"} devi@23: } @index; devi@23: devi@23: print "...finished\n" if $Config{"verbose"} =~ /y/; devi@23: devi@23: } devi@23: devi@23: sub process_command_lines devi@23: { devi@23: for my $i (@Command_Lines_Index) { devi@23: devi@23: my $cl = \$Command_Lines[$i]; devi@23: @{${$cl}->{"new_commands"}} =(); devi@23: @{${$cl}->{"new_files"}} =(); devi@23: $$cl->{"class"} = ""; devi@23: devi@23: if ($$cl->{"err"}) { devi@23: $$cl->{"class"}="wrong"; devi@23: $$cl->{"class"}="interrupted" devi@23: if ($$cl->{"err"} eq 130); devi@23: } devi@23: if (!$$cl->{"euid"}) { devi@23: $$cl->{"class"}.="_root"; devi@23: } devi@23: devi@23: #tab# my @tab_words=split /\s+/, $$cl->{"output"}; devi@23: #tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/; devi@23: #tab# $last_word =~ s@.*/@@; devi@23: #tab# my $this_is_tab=1; devi@23: #tab# devi@23: #tab# if ($last_word && @tab_words >2) { devi@23: #tab# for my $tab_words (@tab_words) { devi@23: #tab# if ($tab_words !~ /^$last_word/) { devi@23: #tab# $this_is_tab=0; devi@23: #tab# last; devi@23: #tab# } devi@23: #tab# } devi@23: #tab# } devi@23: #tab# $$cl->{"class"}="tab" if $this_is_tab; devi@23: devi@23: devi@23: if ( !$$cl->{"err"}) { devi@23: # Command does not contain mistakes devi@23: devi@23: my %commands = extract_from_cline("commands", ${$cl}->{"cline"}); devi@23: my %files = extract_from_cline("files", ${$cl}->{"cline"}); devi@23: devi@23: # Searching for new commands only devi@23: for my $command (keys %commands) { devi@23: if (!defined $Commands_Stat{$command}) { devi@23: push @{$$cl->{new_commands}}, $command; devi@23: } devi@23: $Commands_Stat{$command}++; devi@23: } devi@23: devi@23: for my $file (keys %files) { devi@23: if (!defined $Files_Stat{$file}) { devi@23: push @{$$cl->{new_files}}, $file; devi@23: } devi@23: $Files_Stat{$file}++; devi@23: } devi@23: } devi@23: } devi@23: devi@23: } devi@23: devi@23: devi@23: =cut devi@23: Вывести результат обработки журнала. devi@23: =cut devi@23: devi@23: devi@23: sub print_command_lines devi@23: { devi@23: my $output_filename=$_[0]; devi@25: my $mode = ">"; devi@25: $mode =">>" if $Config{mode} eq "daemon"; devi@25: open(OUT, $mode, $output_filename) devi@23: or die "Can't open $output_filename for writing\n"; devi@23: devi@23: devi@23: devi@25: #print OUT "\n"; devi@23: devi@23: my $cl; devi@23: my $in_range=0; devi@23: for my $i (@Command_Lines_Index) { devi@23: $cl = $Command_Lines[$i]; devi@23: devi@23: if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { devi@23: $in_range=1; devi@23: next; devi@23: } devi@23: if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { devi@23: $in_range=0; devi@23: next; devi@23: } devi@23: next if ($Config{"from"} && $Config{"to"} && !$in_range) devi@23: || devi@23: ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) devi@23: || devi@23: ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) devi@23: || devi@23: ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); devi@23: devi@23: my @new_commands=@{$cl->{"new_commands"}}; devi@23: my @new_files=@{$cl->{"new_files"}}; devi@23: devi@23: my $cl_class="cline"; devi@23: my $out_class="output"; devi@23: if ($cl->{"class"}) { devi@23: $cl_class = $cl->{"class"}."_".$cl_class; devi@23: $out_class = $cl->{"class"}."_".$out_class; devi@23: } devi@23: devi@23: # Вырезаем из вывода только нужное количество строк devi@23: devi@23: my $output=""; devi@23: if ($Config{"head_lines"} || $Config{"tail_lines"}) { devi@23: # Partialy output devi@23: my @lines = split '\n', $cl->{"output"}; devi@23: # head devi@23: my $mark=1; devi@23: for (my $i=0; $i<= $#lines && $i < $Config{"cache_head_lines"}; $i++) { devi@23: $output .= $lines[$i]."\n"; devi@23: } devi@23: # tail devi@23: my $start=$#lines-$Config{"cache_tail_lines"}+1; devi@23: if ($start < 0) { devi@23: $start=0; devi@23: $mark=0; devi@23: } devi@23: if ($start < $Config{"cache_head_lines"}) { devi@23: $start=$Config{"cache_head_lines"}; devi@23: $mark=0; devi@23: } devi@23: $output .= $Config{"skip_text"}."\n" if $mark; devi@23: for (my $i=$start; $i<= $#lines; $i++) { devi@23: $output .= $lines[$i]."\n"; devi@23: } devi@23: } devi@23: else { devi@23: # Full output devi@23: $output .= $cl->{"output"}; devi@23: } devi@23: $output .= "^C\n" if ($cl->{"err"} eq "130"); devi@23: devi@23: devi@23: # Совместимость с labmaker devi@23: devi@23: # Переводим в секунды Эпохи devi@23: # В labmaker'е данные хранились в неудобной форме: hour, min, sec, day of year devi@23: # Информация о годе отсутствовала devi@23: # Её можно внести: devi@23: # Декабрь 2004 год; остальные -- 2005 год. devi@23: devi@23: my $year = 2005; devi@23: $year = 2004 if ( $cl->{day} > 330 ); devi@23: # timelocal( $sec, $min, $hour, $mday,$mon,$year); devi@23: $cl->{time} = timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year); devi@23: devi@23: devi@23: # Начинаем вывод команды devi@23: print OUT "\n"; devi@27: print OUT "",$cl->{session_id},"\n"; devi@23: print OUT "\n"; devi@25: print OUT "",$cl->{raw_start},"\n"; devi@25: print OUT "",$cl->{raw_output_start},"\n"; devi@25: print OUT "",$cl->{raw_end},"\n"; devi@25: print OUT "",$cl->{raw_file},"\n"; devi@23: print OUT "",$cl->{tty},"\n"; devi@23: print OUT "",$out_class,"\n"; devi@23: print OUT ""; devi@23: printq(\*OUT,,$cl->{"prompt"}); devi@23: print OUT ""; devi@23: print OUT ""; devi@23: printq(\*OUT,$cl->{"cline"}); devi@23: print OUT "\n"; devi@23: print OUT "",$cl->{"last_command"},"\n"; devi@23: if (@new_commands) { devi@23: print OUT ""; devi@23: printq(\*OUT, join (" ", @new_commands)); devi@23: print OUT ""; devi@23: } devi@23: if (@new_files) { devi@23: print OUT ""; devi@23: printq(\*OUT, join (" ", @new_files)); devi@23: print OUT ""; devi@23: } devi@23: print OUT ""; devi@23: printq(\*OUT,$output); devi@23: print OUT "\n"; devi@23: if ($cl->{"diff"}) { devi@23: print OUT ""; devi@23: printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"}); devi@23: print OUT "\n"; devi@23: } devi@23: print OUT "\n"; devi@23: devi@23: } devi@23: devi@25: #print OUT "\n"; devi@23: close(OUT); devi@27: } devi@27: devi@27: sub print_session devi@27: { devi@27: my $output_filename = $_[0]; devi@27: my $local_session_id = $_[1]; devi@27: return if not defined($Sessions{$local_session_id}); devi@27: devi@27: open(OUT, ">>", $output_filename) devi@27: or die "Can't open $output_filename for writing\n"; devi@27: print OUT "\n"; devi@27: my %session = %{$Sessions{$local_session_id}}; devi@27: for my $key (keys %session) { devi@27: print OUT "<$key>".$session{$key}."\n" devi@27: } devi@27: print OUT "\n"; devi@27: close(OUT); devi@27: } devi@27: devi@27: sub send_cache devi@27: { devi@27: `logger "step 0"`; devi@27: `logger "step 1"`; devi@27: devi@27: # Если в кэше что-то накопилось, devi@27: # попытаемся отправить это на сервер devi@27: # devi@27: my $cache_was_sent=0; devi@27: devi@27: if (open(CACHE, $Config{cache})) { devi@27: local $/; devi@27: my $cache = ; devi@27: close(CACHE); devi@27: devi@27: my $socket = IO::Socket::INET->new( devi@27: PeerAddr => $Config{backend_address}, devi@27: PeerPort => $Config{backend_port}, devi@27: proto => "tcp", devi@27: Type => SOCK_STREAM devi@27: ); devi@27: devi@27: if ($socket) { devi@27: print $socket $cache; devi@27: close($socket); devi@27: $cache_was_sent = 1; devi@27: } devi@27: } devi@27: return $cache_was_sent; devi@23: } devi@23: devi@25: sub save_cache_stat devi@25: { devi@25: open (CACHE, ">$Config{cache_stat}"); devi@25: for my $f (keys %Script_Files) { devi@25: print CACHE "$f\t",$Script_Files{$f}->{size},"\t",$Script_Files{$f}->{tell},"\n"; devi@25: } devi@25: close(CACHE); devi@25: } devi@25: devi@25: sub load_cache_stat devi@25: { devi@25: if (open (CACHE, "$Config{cache_stat}")) { devi@25: while() { devi@27: chomp; devi@25: my ($f, $size, $tell) = split /\t/; devi@25: $Script_Files{$f}->{size} = $size; devi@25: $Script_Files{$f}->{tell} = $tell; devi@25: } devi@25: close(CACHE); devi@25: }; devi@25: } devi@23: devi@23: devi@23: main(); devi@23: devi@25: sub process_was_killed devi@25: { devi@25: $Killed = 1; devi@25: } devi@25: devi@23: sub main devi@23: { devi@23: devi@27: $| = 1; devi@23: devi@27: init_variables(); devi@27: init_config(); devi@23: devi@27: for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) { devi@27: load_diff_files($lab_log); devi@27: } devi@27: devi@27: if ($Config{"mode"} ne "daemon") { devi@27: devi@27: =cut devi@27: В нормальном режиме работы нужно devi@27: считать скрипты, обработать их и записать devi@27: результат выполнения в результриующий файл. devi@27: После этого завершить работу. devi@27: =cut devi@27: load_command_lines($Config{"input"}, $Config{"input_mask"}); devi@27: sort_command_lines; devi@27: process_command_lines; devi@27: print_command_lines($Config{"cache"}); devi@27: } devi@27: else { devi@27: if (open(PIDFILE, $Config{agent_pidfile})) { devi@27: my $pid = ; devi@27: close(PIDFILE); devi@27: if ( ! -e "/proc/$pid" || !`grep $Config{"l3-agent"} /proc/$pid/cmdline && grep "uid:.*\b$<\b" /proc/$pid/status`) { devi@27: print "Removing stale pidfile\n"; devi@27: unlink $Config{agent_pidfile} devi@27: or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!"; devi@27: } devi@27: else { devi@27: print "l3-agent is already running\n"; devi@27: exit(0); devi@27: } devi@25: } devi@27: if ($Config{detach} =~ /^y/i) { devi@27: #$Config{verbose} = "no"; devi@27: my $pid = fork; devi@27: exit if $pid; devi@27: die "Couldn't fork: $!" unless defined ($pid); devi@27: devi@27: open(PIDFILE, ">", $Config{agent_pidfile}) devi@27: or die "Can't open pidfile ". $Config{agent_pidfile}. " for wrting: $!"; devi@27: print PIDFILE $$; devi@27: close(PIDFILE); devi@27: devi@27: for my $handle (*STDIN, *STDOUT, *STDERR) { devi@27: open ($handle, "+<", "/dev/null") devi@27: or die "can't reopen $handle to /dev/null: $!" devi@27: } devi@27: devi@27: POSIX::setsid() devi@27: or die "Can't start a new session: $!"; devi@27: devi@27: $0 = $Config{"l3-agent"}; devi@27: devi@27: $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&process_was_killed; devi@25: } devi@27: while (not $Killed) { devi@27: @Command_Lines = (); devi@27: @Command_Lines_Index = (); devi@27: load_cache_stat(); devi@27: load_command_lines($Config{"input"}, $Config{"input_mask"}); devi@27: if (@Command_Lines) { devi@27: sort_command_lines; devi@27: process_command_lines; devi@27: print_command_lines($Config{"cache"}); devi@27: } devi@27: save_cache_stat(); devi@27: if (-e $Config{cache} && (stat($Config{cache}))[7]) { devi@27: send_cache() && unlink($Config{cache}); devi@27: } devi@27: sleep($Config{"daemon_sleep_interval"} || 1); devi@27: } devi@27: devi@27: unlink $Config{agent_pidfile}; devi@25: } devi@23: devi@23: } devi@23: devi@23: sub init_variables devi@23: { devi@23: }