lilalo

view l3-agent @ 100:2c00c61f2d7b

Коммичу изменения, но сам не знаю зачем.
Нужно l3-cgi переписать вообще с нуля.
Он мерзкий.

И продумать нужно, как он вообще должен работать.
Понятно, приблизительно, как он должен показывать журнал,
когда до него уже дошли,
но вот если не дошли, то что делать не понятно.
Короче, продумать систему навигации.
author devi
date Wed Jun 14 21:37:22 2006 +0300 (2006-06-14)
parents 3058ada85a58
children 0d49f33696b3
line source
1 #!/usr/bin/perl -w
3 #
4 # (c) Igor Chubin, igor@chub.in, 2004-2006
5 #
8 ## Эта строчка добавлена из блокнота Windows
9 ## Надо отдать должное, он каким-то образом научился понимать кодировку
11 use strict;
12 use POSIX;
13 use Term::VT102;
14 use Text::Iconv;
15 use Time::Local 'timelocal_nocheck';
16 use IO::Socket;
18 use lib "/usr/local/bin";
19 use l3config;
22 our @Command_Lines;
23 our @Command_Lines_Index;
24 our %Diffs;
25 our %Sessions;
27 our %Script_Files; # Информация о позициях в скрипт-файлах,
28 # до которых уже выполнен разбор
29 # и информация о времени модификации файла
30 # $Script_Files{$file}->{size}
31 # $Script_Files{$file}->{tell}
33 our $Killed =0; # В режиме демона -- процесс получил сигнал о завершении
35 sub init_variables;
36 sub main;
38 sub load_diff_files;
39 sub bind_diff;
40 sub extract_commands_from_cline;
41 sub load_command_lines;
42 sub sort_command_lines;
43 sub print_command_lines;
44 sub printq;
46 sub save_cache_stat;
47 sub load_cache_stat;
48 sub print_session;
50 sub load_diff_files
51 {
52 my @pathes = @_;
54 for my $path (@pathes) {
55 my $template = "*.diff";
56 my @files = <$path/$template>;
57 my $i=0;
58 for my $file (@files) {
60 next if defined($Diffs{$file});
61 my %diff;
63 # Старый формат имени diff-файла
64 # DEPRECATED
65 if ($file=~m@/(D?[0-9][0-9]?[0-9]?)[^/]*?([0-9]*):([0-9]*):?([0-9]*)@) {
66 $diff{"day"}=$1 || "";
67 $diff{"hour"}=$2;
68 $diff{"min"}=$3;
69 $diff{"sec"}=$4 || 0;
71 $diff{"uid"} = 0 if $path =~ m@/root/@;
73 print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n";
75 }
76 # Новый формат имени diff-файла
77 elsif ($file =~ m@.*/([^_]*)_([0-9]+)(.*)@) {
78 $diff{"local_session_id"} = $1;
79 $diff{"time"} = $2;
80 $diff{"filename"} = $3;
81 $diff{"filename"} =~ s@_@/@g;
82 $diff{"filename"} =~ s@//@_@g;
84 print "diff loaded: $diff{filename} (time=$diff{time},session=$diff{local_session_id})\n";
85 }
86 else {
87 next;
88 }
90 # Чтение и изменение кодировки содержимого diff-файла
91 local $/;
92 open (F, "$file")
93 or return "Can't open file $file ($_[0]) for reading";
94 my $text = <F>;
95 if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i) {
96 my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8");
97 $text = $converter->convert($text);
98 }
99 close(F);
100 $diff{"text"}=$text;
102 $diff{"path"}=$path;
103 $diff{"bind_to"}="";
104 $diff{"time_range"}=-1;
105 $diff{"index"}=$i;
107 $Diffs{$file} = \%diff;
108 $i++;
109 }
110 }
111 }
114 sub bind_diff
115 {
116 print "Trying to bind diff...\n";
118 my $cl = shift;
119 my $hour = $cl->{"hour"};
120 my $min = $cl->{"min"};
121 my $sec = $cl->{"sec"};
123 my $min_dt = 10000;
125 for my $diff_key (keys %Diffs) {
126 my $diff = $Diffs{$diff_key};
127 next if ($diff->{"local_session_id"}
128 && $cl->{"local_session_id"}
129 && ($cl->{"local_session_id"} ne $diff->{"local_session_id"}));
131 next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"}));
133 my $dt;
134 if ($diff->{"time"} && $cl->{"time"}) {
135 $dt = $diff->{"time"} - $cl->{"time"}
136 }
137 else {
138 $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec);
139 }
140 if ($dt >0
141 && $dt < $min_dt
142 && ($diff->{"time_range"} <0
143 || $dt < $diff->{"time_range"})) {
144 print "Approppriate diff found: dt=$dt\n";
145 if ($diff->{"bind_to"}) {
146 undef $diff->{"bind_to"}->{"diff"};
147 };
148 $diff->{"time_range"}=$dt;
149 $diff->{"bind_to"}=$cl;
151 $cl->{"diff"} = $diff_key;
152 $min_dt = $dt;
153 }
154 }
155 }
158 sub extract_commands_from_cline
159 # Разобрать командную строку $_[1] и возвратить хэш, содержащий
160 # номер первого появление команды в строке:
161 # команда => первая позиция
162 {
163 my $cline = $_[0];
164 my @lists = split /\;/, $cline;
167 my @commands = ();
168 for my $list (@lists) {
169 push @commands, split /\|/, $list;
170 }
172 my %commands;
173 my %files;
174 my $i=0;
175 for my $command (@commands) {
176 $command =~ /\s*(\S+)\s*(.*)/;
177 if ($1 && $1 eq "sudo" ) {
178 $commands{"$1"}=$i++;
179 $command =~ s/\s*sudo\s+//;
180 }
181 $command =~ /\s*(\S+)\s*(.*)/;
182 if ($1 && !defined $commands{"$1"}) {
183 $commands{"$1"}=$i++;
184 };
185 }
186 return %commands;
187 }
189 sub load_command_lines
190 {
191 my $lab_scripts_path = $_[0];
192 my $lab_scripts_mask = $_[1];
194 my $cline_re_base = qq'
195 (
196 (?:\\^?([0-9]*C?)) # exitcode
197 (?:_([0-9]+)_)? # uid
198 (?:_([0-9]+)_) # pid
199 (...?) # day
200 (.?.?) # lab
201 \\s # space separator
202 ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]) # time
203 .\\[50D.\\[K # killing symbols
204 (.*?([\$\#]\\s?)) # prompt
205 (.*) # command line
206 )
207 ';
208 my $cline_re = qr/$cline_re_base/sx;
209 my $cline_re2 = qr/$cline_re_base$/sx;
211 my $cline_re_v2_base = qq'
212 (
213 v2[\#] # version
214 ([0-9]+)[\#] # history line number
215 ([0-9]+)[\#] # exitcode
216 ([0-9]+)[\#] # uid
217 ([0-9]+)[\#] # pid
218 ([0-9]+)[\#] # time
219 (.*?)[\#] # pwd
220 .\\[1024D.\\[K # killing symbols
221 (.*?([\$\#]\\s?)) # prompt
222 (.*) # command line
223 )
224 ';
226 my $cline_re_v2 = qr/$cline_re_v2_base/sx;
227 my $cline_re2_v2 = qr/$cline_re_v2_base$/sx;
229 my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"},
230 'rows' => $Config{"terminal_height"});
231 my $cline_vt = Term::VT102->new (
232 'cols' => $Config{"terminal_width"},
233 'rows' => $Config{"terminal_height"});
235 my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8")
236 if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i);
238 print "Parsing lab scripts...\n" if $Config{"verbose"} =~ /y/;
240 my $file;
241 my $skip_info;
243 my $commandlines_loaded =0;
244 my $commandlines_processed =0;
246 my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>;
247 for $file (@lab_scripts){
249 # Пропускаем файл, если он не изменялся со времени нашего предудущего прохода
250 my $size = (stat($file))[7];
251 next if ($Script_Files{$file} && $Script_Files{$file}->{size} && $Script_Files{$file}->{size} >= $size);
254 my $local_session_id;
255 # Начальное значение идентификатора текущего сеанса определяем из имени скрипта
256 # Впоследствии оно может быть уточнено
257 $file =~ m@.*/([^/]*)\.script$@;
258 $local_session_id = $1;
260 #Если файл только что появился,
261 #пытаемся найти и загрузить информацию о соответствующей ему сессии
262 if (!$Script_Files{$file}) {
263 my $session_file = $file;
264 $session_file =~ s/\.script/.info/;
265 if (open(SESSION, $session_file)) {
266 local $/;
267 my $data = <SESSION>;
268 close(SESSION);
270 for my $session_data ($data =~ m@<session>(.*?)</session>@sg) {
271 my %session;
272 while ($session_data =~ m@<([^>]*?)>(.*?)</\1>@sg) {
273 $session{$1} = $2;
274 }
275 $local_session_id = $session{"local_session_id"} if $session{"local_session_id"};
276 $Sessions{$local_session_id}=\%session;
277 }
279 #Загруженную информацию сразу же отправляем в поток
280 print_session($Config{cache}, $local_session_id);
281 }
282 else {
283 die "can't open session file";
284 }
285 }
287 open (FILE, "$file");
288 binmode FILE;
290 # Переходим к тому месту, где мы окончили разбор
291 seek (FILE, $Script_Files{$file}->{tell}, 0) if $Script_Files{$file}->{tell};
292 $Script_Files{$file}->{size} = $size;
293 $Script_Files{$file}->{tell} = 0 unless $Script_Files{$file}->{tell};
295 $file =~ m@.*/(.*?)-.*@;
297 print "\n+- processing file $file\n| "
298 if $Config{"verbose"} =~/y/;
300 my $tty = $1;
301 my $first_pass = 1;
302 my %cl;
303 my $last_output_length=0;
304 while (<FILE>) {
305 $commandlines_processed++;
307 next if s/^Script started on.*?\n//s;
309 if (/[0-9][0-9]:[0-9][0-9]:[0-9][0-9].\[[0-9][0-9]D.\[K/ && m/$cline_re/) {
310 s/.*\x0d(?!\x0a)//;
311 m/$cline_re2/gs;
313 $commandlines_loaded++;
314 $last_output_length=0;
316 # Previous command
317 my %last_cl = %cl;
318 my $err = $2 || "";
320 $cl{"local_session_id"} = $local_session_id;
321 # Parse new command
322 $cl{"uid"} = $3;
323 #$cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0
324 $cl{"pid"} = $4;
325 $cl{"day"} = $5;
326 $cl{"lab"} = $6;
327 $cl{"hour"} = $7;
328 $cl{"min"} = $8;
329 $cl{"sec"} = $9;
330 #$cl{"fullprompt"} = $10;
331 $cl{"prompt"} = $11;
332 $cl{"raw_cline"} = $12;
334 {
335 use bytes;
336 $cl{"raw_start"} = tell (FILE) - length($1);
337 $cl{"raw_output_start"} = tell FILE;
338 }
339 $cl{"raw_file"} = $file;
341 $cl{"err"} = 0;
342 $cl{"output"} = "";
343 $cl{"tty"} = $tty;
345 $cline_vt->process($cl{"raw_cline"}."\n");
346 $cl{"cline"} = $cline_vt->row_plaintext (1);
347 $cl{"cline"} =~ s/\s*$//;
348 $cline_vt->reset();
350 my %commands = extract_commands_from_cline($cl{"cline"});
351 #$cl{"euid"}=0 if defined $commands{"sudo"};
352 my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands;
353 $cl{"last_command"} = $comms[$#comms] || "";
355 if (
356 $Config{"suppress_editors"} =~ /^y/i
357 && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}})
358 || $Config{"suppress_pagers"} =~ /^y/i
359 && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}})
360 || $Config{"suppress_terminal"}=~ /^y/i
361 && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}})
362 ) {
363 $cl{"suppress_output"} = "1";
364 }
365 else {
366 $cl{"suppress_output"} = "0";
367 }
368 $skip_info = 0;
371 print " ",$cl{"last_command"};
373 # Processing previous command line
374 if ($first_pass) {
375 $first_pass = 0;
376 next;
377 }
379 # Error code
380 $last_cl{"raw_end"} = $cl{"raw_start"};
381 $last_cl{"err"}=$err;
382 $last_cl{"err"}=130 if $err eq "^C";
384 if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) {
385 bind_diff(\%last_cl);
386 }
388 # Output
389 if (!$last_cl{"suppress_output"} || $last_cl{"err"}) {
390 for (my $i=0; $i<$Config{"terminal_height"}; $i++) {
391 my $line= $vt->row_plaintext($i);
392 next if !defined ($line) ; #|| $line =~ /^\s*$/;
393 $line =~ s/\s*$//;
394 $line .= "\n" unless $line =~ /^\s*$/;
395 $last_cl{"output"} .= $line;
396 }
397 }
398 else {
399 $last_cl{"output"}= "";
400 }
402 $vt->reset();
405 # Save
406 if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) {
407 # Changing encoding
408 for (keys %last_cl) {
409 next if /raw/;
410 $last_cl{$_} = $converter->convert($last_cl{$_})
411 if ($Config{"encoding"} &&
412 $Config{"encoding"} !~ /^utf-8$/i);
413 }
414 push @Command_Lines, \%last_cl;
416 # Сохранение позиции в файле, до которой выполнен
417 # успешный разбор
418 $Script_Files{$file}->{tell} = $last_cl{raw_end};
419 }
420 next;
421 }
424 elsif (m/$cline_re_v2/) {
427 # Разбираем командную строку версии 2
430 s/.*\x0d(?!\x0a)//;
431 m/$cline_re2_v2/gs;
433 $commandlines_loaded++;
434 $last_output_length=0;
436 # Previous command
437 my %last_cl = %cl;
439 $cl{"local_session_id"} = $local_session_id;
440 # Parse new command
441 $cl{"history"} = $2;
442 my $err = $3;
443 $cl{"uid"} = $4;
444 #$cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0
445 $cl{"pid"} = $5;
446 $cl{"time"} = $6;
447 $cl{"pwd"} = $7;
448 #$cl{"fullprompt"} = $8;
449 $cl{"prompt"} = $9;
450 $cl{"raw_cline"}= $10;
452 {
453 use bytes;
454 $cl{"raw_start"} = tell (FILE) - length($1);
455 $cl{"raw_output_start"} = tell FILE;
456 }
457 $cl{"raw_file"} = $file;
459 $cl{"err"} = 0;
460 $cl{"output"} = "";
461 #$cl{"tty"} = $tty;
463 $cline_vt->process($cl{"raw_cline"}."\n");
464 $cl{"cline"} = $cline_vt->row_plaintext (1);
465 $cl{"cline"} =~ s/\s*$//;
466 $cline_vt->reset();
468 my %commands = extract_commands_from_cline($cl{"cline"});
469 #$cl{"euid"} = 0 if defined $commands{"sudo"};
470 my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands;
471 $cl{"last_command"}
472 = $comms[$#comms] || "";
474 if (
475 $Config{"suppress_editors"} =~ /^y/i
476 && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}})
477 || $Config{"suppress_pagers"} =~ /^y/i
478 && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}})
479 || $Config{"suppress_terminal"}=~ /^y/i
480 && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}})
481 ) {
482 $cl{"suppress_output"} = "1";
483 }
484 else {
485 $cl{"suppress_output"} = "0";
486 }
487 $skip_info = 0;
490 if ($Config{verbose} =~ /y/i) {
491 print "\n| " if $commandlines_loaded % 5 == 1;
492 print " ",$cl{"last_command"};
493 }
495 # Processing previous command line
496 if ($first_pass) {
497 $first_pass = 0;
498 next;
499 }
501 # Error code
502 $last_cl{"err"}=$err;
503 $last_cl{"raw_end"} = $cl{"raw_start"};
505 if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) {
506 bind_diff(\%last_cl);
507 }
509 # Output
510 if (!$last_cl{"suppress_output"} || $last_cl{"err"}) {
511 for (my $i=0; $i<$Config{"terminal_height"}; $i++) {
512 my $line= $vt->row_plaintext($i);
513 next if !defined ($line) ; #|| $line =~ /^\s*$/;
514 $line =~ s/\s*$//;
515 $line .= "\n" unless $line =~ /^\s*$/;
516 $last_cl{"output"} .= $line;
517 }
518 }
519 else {
520 $last_cl{"output"}= "";
521 }
523 $vt->reset();
526 # Changing encoding
527 for (keys %last_cl) {
528 next if /raw/;
529 if ($Config{"encoding"} &&
530 $Config{"encoding"} !~ /^utf-8$/i) {
531 $last_cl{$_} = $converter->convert($last_cl{$_})
532 }
533 }
534 push @Command_Lines, \%last_cl;
536 # Сохранение позиции в файле, до которой выполнен
537 # успешный разбор
538 $Script_Files{$file}->{tell} = $last_cl{raw_end};
540 next;
542 }
544 # Иначе, это строка вывода
546 $last_output_length+=length($_);
547 #if (!$cl{"suppress_output"} || $last_output_length < 5000) {
548 if ($last_output_length < 50000) {
549 $vt->process("$_"."\n")
550 }
551 else
552 {
553 if (!$skip_info) {
554 print "($cl{last_command})";
555 $skip_info = 1;
556 }
557 }
558 }
559 close(FILE);
561 }
562 if ($Config{"verbose"} =~ /y/) {
563 print "\n`- finished.\n" ;
564 print "Lines loaded: $commandlines_processed\n";
565 print "Command lines: $commandlines_loaded\n";
566 }
567 }
572 sub sort_command_lines
573 {
574 print "Sorting command lines..." if $Config{"verbose"} =~ /y/;
576 # Sort Command_Lines
577 # Write Command_Lines to Command_Lines_Index
579 my @index;
580 for (my $i=0;$i<=$#Command_Lines;$i++) {
581 $index[$i]=$i;
582 }
584 @Command_Lines_Index = sort {
585 defined($Command_Lines[$index[$a]]->{"time"})
586 && defined($Command_Lines[$index[$b]]->{"time"})
587 ? $Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"}
588 : defined($Command_Lines[$index[$a]]->{"day"})
589 && defined($Command_Lines[$index[$b]]->{"day"})
590 && defined($Command_Lines[$index[$a]]->{"hour"})
591 && defined($Command_Lines[$index[$b]]->{"hour"})
592 && defined($Command_Lines[$index[$a]]->{"min"})
593 && defined($Command_Lines[$index[$b]]->{"min"})
594 && defined($Command_Lines[$index[$a]]->{"sec"})
595 && defined($Command_Lines[$index[$b]]->{"sec"})
596 ? $Command_Lines[$index[$a]]->{"day"} cmp $Command_Lines[$index[$b]]->{"day"}
597 || $Command_Lines[$index[$a]]->{"hour"} <=> $Command_Lines[$index[$b]]->{"hour"}
598 || $Command_Lines[$index[$a]]->{"min"} <=> $Command_Lines[$index[$b]]->{"min"}
599 || $Command_Lines[$index[$a]]->{"sec"} <=> $Command_Lines[$index[$b]]->{"sec"}
600 : 0
601 } @index;
603 print "finished\n" if $Config{"verbose"} =~ /y/;
605 }
607 sub printq
608 {
609 my $TO = shift;
610 my $text = join "", @_;
611 $text =~ s/&/&amp;/g;
612 $text =~ s/</&lt;/g;
613 $text =~ s/>/&gt;/g;
614 print $TO $text;
615 }
618 =cut
619 Вывести результат обработки журнала.
620 =cut
622 sub print_command_lines
623 {
624 my $output_filename=$_[0];
625 open(OUT, ">>", $output_filename)
626 or die "Can't open $output_filename for writing\n";
629 my $cl;
630 my $in_range=0;
631 for my $i (@Command_Lines_Index) {
632 $cl = $Command_Lines[$i];
634 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
635 $in_range=1;
636 next;
637 }
638 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
639 $in_range=0;
640 next;
641 }
642 next if ($Config{"from"} && $Config{"to"} && !$in_range)
643 ||
644 ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
645 ||
646 ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
647 ||
648 ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
650 # Вырезаем из вывода только нужное количество строк
652 my $output="";
654 if (!grep ($_ eq $cl->{"last_command"}, @{$Config{"full_output_commands"}})
655 && ($Config{"head_lines"}
656 || $Config{"tail_lines"})) {
657 # Partialy output
658 my @lines = split '\n', $cl->{"output"};
659 # head
660 my $mark=1;
661 for (my $i=0; $i<= $#lines && $i < $Config{"cache_head_lines"}; $i++) {
662 $output .= $lines[$i]."\n";
663 }
664 # tail
665 my $start=$#lines-$Config{"cache_tail_lines"}+1;
666 if ($start < 0) {
667 $start=0;
668 $mark=0;
669 }
670 if ($start < $Config{"cache_head_lines"}) {
671 $start=$Config{"cache_head_lines"};
672 $mark=0;
673 }
674 $output .= $Config{"skip_text"}."\n" if $mark;
675 for ($i=$start; $i<= $#lines; $i++) {
676 $output .= $lines[$i]."\n";
677 }
678 }
679 else {
680 # Full output
681 $output .= $cl->{"output"};
682 }
684 # Совместимость с labmaker
686 # Переводим в секунды Эпохи
687 # В labmaker'е данные хранились в неудобной форме: hour, min, sec, day of year
688 # Информация о годе отсутствовала
689 # Её можно внести:
690 # Декабрь 2004 год; остальные -- 2005 год.
692 my $year = 2005;
693 #$year = 2004 if ( $cl->{day} > 330 );
694 $year = $Config{year} if $Config{year};
695 # timelocal( $sec, $min, $hour, $mday,$mon,$year);
696 $cl->{time} ||= timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year);
699 # Начинаем вывод команды
700 print OUT "<command>\n";
701 print OUT "<l3cd>$Config{l3cd}</l3cd>\n" if $Config{"l3cd"};
702 for my $element (qw(
703 local_session_id
704 history
705 uid
706 pid
707 time
708 pwd
709 raw_start
710 raw_output_start
711 raw_end
712 raw_file
713 tty
714 err
715 last_command
716 history
717 )) {
718 next unless defined($cl->{"$element"});
719 print OUT "<$element>".$cl->{$element}."</$element>\n";
720 }
721 for my $element (qw(
722 prompt
723 cline
724 )) {
725 next unless defined($cl->{"$element"});
726 print OUT "<$element>";
727 printq(\*OUT,$cl->{"$element"});
728 print OUT "</$element>\n";
729 }
730 #note
731 #note_title
732 print OUT "<output>";
733 printq(\*OUT,$output);
734 print OUT "</output>\n";
735 if ($cl->{"diff"}) {
736 print OUT "<diff>";
737 printq(\*OUT,${$Diffs{$cl->{"diff"}}}{"text"});
738 print OUT "</diff>\n";
739 }
740 print OUT "</command>\n";
742 }
744 close(OUT);
745 }
747 sub print_session
748 {
749 my $output_filename = $_[0];
750 my $local_session_id = $_[1];
751 return if not defined($Sessions{$local_session_id});
753 print "printing session info. session id = ".$local_session_id."\n"
754 if $Config{verbose} =~ /y/;
756 open(OUT, ">>", $output_filename)
757 or die "Can't open $output_filename for writing\n";
758 print OUT "<session>\n";
759 print OUT "<l3cd>$Config{l3cd}</l3cd>\n" if $Config{"l3cd"};
760 my %session = %{$Sessions{$local_session_id}};
761 for my $key (keys %session) {
762 print OUT "<$key>".$session{$key}."</$key>\n";
763 print " ".$key,"\n";
764 }
765 print OUT "</session>\n";
766 close(OUT);
767 }
769 sub send_cache
770 {
771 # Если в кэше что-то накопилось,
772 # попытаемся отправить это на сервер
773 #
774 my $cache_was_sent=0;
776 if (open(CACHE, $Config{cache})) {
777 local $/;
778 my $cache = <CACHE>;
779 close(CACHE);
781 my $socket = IO::Socket::INET->new(
782 PeerAddr => $Config{backend_address},
783 PeerPort => $Config{backend_port},
784 proto => "tcp",
785 Type => SOCK_STREAM
786 );
788 if ($socket) {
789 print $socket $cache;
790 close($socket);
791 $cache_was_sent = 1;
792 }
793 }
794 return $cache_was_sent;
795 }
797 sub save_cache_stat
798 {
799 open (CACHE, ">$Config{cache_stat}");
800 for my $f (keys %Script_Files) {
801 print CACHE "$f\t",$Script_Files{$f}->{size},"\t",$Script_Files{$f}->{tell},"\n";
802 }
803 close(CACHE);
804 }
806 sub load_cache_stat
807 {
808 if (open (CACHE, "$Config{cache_stat}")) {
809 while(<CACHE>) {
810 chomp;
811 my ($f, $size, $tell) = split /\t/;
812 $Script_Files{$f}->{size} = $size;
813 $Script_Files{$f}->{tell} = $tell;
814 }
815 close(CACHE);
816 };
817 }
820 main();
822 sub process_was_killed
823 {
824 $Killed = 1;
825 }
827 sub reload
828 {
829 init_config;
830 }
832 sub main
833 {
835 $| = 1;
837 init_variables();
838 init_config();
841 if ($Config{"mode"} ne "daemon") {
843 # В нормальном режиме работы нужно
844 # считать скрипты, обработать их и записать
845 # результат выполнения в результирующий файл.
846 # После этого завершить работу.
848 # Очистим кэш-файл, если он существовал
849 if (open (CACHE, ">", $Config{"cache"})) {
850 close(CACHE);
851 };
852 for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) {
853 load_diff_files($lab_log);
854 }
855 load_command_lines($Config{"input"}, $Config{"input_mask"});
856 sort_command_lines;
857 #process_command_lines;
858 print_command_lines($Config{"cache"});
859 }
860 else {
861 if (open(PIDFILE, $Config{agent_pidfile})) {
862 my $pid = <PIDFILE>;
863 close(PIDFILE);
864 if ($^O eq 'linux' && $pid &&(! -e "/proc/$pid" || !`grep $Config{"l3-agent"} /proc/$pid/cmdline && grep "uid:.*\b$<\b" /proc/$pid/status`)) {
865 print "Removing stale pidfile\n";
866 unlink $Config{agent_pidfile}
867 or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!";
868 }
869 elsif ($^O eq 'freebsd' && $pid && `ps axo uid,pid,command | grep '$<\\s*$pid\\s*$Config{"l3-agent"}' 2> /dev/null`) {
870 print "Removing stale pidfile\n";
871 unlink $Config{agent_pidfile}
872 or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!";
873 }
874 elsif ($^O eq 'linux' || $^O eq 'freebsd' ) {
875 print "l3-agent is already running: pid=$pid; pidfile=$Config{agent_pidfile}\n";
876 exit(0);
877 }
878 else {
879 print "Unknown operating system";
880 exit(0);
881 }
882 }
883 if ($Config{detach} =~ /^y/i) {
884 #$Config{verbose} = "no";
885 my $pid = fork;
886 exit if $pid;
887 die "Couldn't fork: $!" unless defined ($pid);
889 open(PIDFILE, ">", $Config{agent_pidfile})
890 or die "Can't open pidfile ". $Config{agent_pidfile}. " for wrting: $!";
891 print PIDFILE $$;
892 close(PIDFILE);
894 for my $handle (*STDIN, *STDOUT, *STDERR) {
895 open ($handle, "+<", "/dev/null")
896 or die "can't reopen $handle to /dev/null: $!"
897 }
899 POSIX::setsid()
900 or die "Can't start a new session: $!";
902 $0 = $Config{"l3-agent"};
904 $SIG{INT} = $SIG{TERM} = \&process_was_killed;
905 $SIG{HUP} = \&reload;
907 }
908 while (not $Killed) {
909 @Command_Lines = ();
910 @Command_Lines_Index = ();
911 for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) {
912 load_diff_files($lab_log);
913 }
914 load_cache_stat();
915 load_command_lines($Config{"input"}, $Config{"input_mask"});
916 if (@Command_Lines) {
917 sort_command_lines;
918 #process_command_lines;
919 print_command_lines($Config{"cache"});
920 }
921 save_cache_stat();
922 if (-e $Config{cache} && (stat($Config{cache}))[7]) {
923 send_cache() && unlink($Config{cache});
924 }
925 sleep($Config{"daemon_sleep_interval"} || 1);
926 }
928 unlink $Config{agent_pidfile};
929 }
931 }
933 sub init_variables
934 {
935 }