lilalo

annotate l3-agent @ 25:ba4d6515b8fd

Выполнен шаг (3) в плане (N05) по построению распределённой системы lilalo.


Агент l3-агент в реальном времени анализирует скрипты в указанном ему
каталоге и по мере обнаружения новых завершённых команд записывает их
в кэш-файл.

Данные о том, докуда разобран каждый скрипт-файл сохраняются во временном
файле, для того чтобы при перезапуске агента он мог продолжить разбор
с того места, где он был остановлен в прошлый раз, а не копировал
данные в кэш-файл повторно.

Агент запускается для каждого пользователя системы.
Если агент обнаружил свою копию работающую от имени того же пользователя,
он автоматически завершается.
Поиск копии агента выполняется так:
просматривается pid-файл агента - если его нет, считается, что и агент не запущен
(Внимание! Не удаляйте pid-файл!! Работа нескольких агентов от имени одного
пользователя может быть некорректной!)
Если он есть, выполняется проверка, действительно ли процесс с таким идентификатором
это l3-агент текущего пользователя. Если нет, pid-файл удаляется, и агент запускается.

Нормальное завершение агента, работающего в режиме демона, выполняется
с помощью сигнала TERM. При завершении агент автоматически стирает свой pid-файл.



Добавлены атрибуты команды, хранящие информацию о участке бинарного файла скрипта,
соответствующей команды:
raw_start - начало блока команды
raw_output_start - начало вывода команды
raw_end - окончание вывода
raw_file - имя бинарного файла

Файлы:
(могут меняться с помощью конфигурационных параметров)

~/.labmaker/.cache.dat
~/.labmaker/cache.xml
~/.labmaker/l3-agent.pid

Конфигурационные параметры:
cache_stat Имя файла с информацией о текущей позиции разбора
в каждом файле

mode Режим, в котором работает агент.
Допустимые значения:
daemon - в режиме непрерывного опроса каталога
Программа не завершается после окончания анализа,
а ждёт появления новых данных
normal - однократный анализ каталога.
Программа завершается после окончания анализа данных

daemon_sleep_interval Интервал через который агент просматривает каталог скриптов
в поисках новых данных

detach Нужно ли выполнять отключение от терминала при работе в режиме демона?
(строго говоря, если процесс не отключился от терминала,
то и в режиме демона он работать не может. Здесь имеется в виду
режим непрерывного опроса каталога)

agent_pidfile Путь к файлу, который будет хранить идентификатор процесса агента.

l3-agent Имя, под которым будет известен процесс l3-agent

Незначительные исправления:
* убрана отладочная информация о new_commands и new_files из frontend'а
author devi
date Thu Nov 03 17:49:56 2005 +0200 (2005-11-03)
parents 6d93c5f1d0e5
children 098664cf339c
rev   line source
devi@23 1 #!/usr/bin/perl -w
devi@23 2
devi@23 3 #
devi@23 4 # (c) Igor Chubin, imchubin@mail.ru, 2004-2005
devi@23 5 #
devi@23 6
devi@23 7 use strict;
devi@25 8 use POSIX;
devi@23 9 use Term::VT102;
devi@23 10 use Text::Iconv;
devi@23 11 use Data::Dumper;
devi@23 12 use Time::Local 'timelocal_nocheck';
devi@23 13
devi@23 14 use lib ".";
devi@23 15 use l3config;
devi@23 16
devi@23 17
devi@23 18 our @Command_Lines;
devi@23 19 our @Command_Lines_Index;
devi@23 20 our @Diffs;
devi@23 21
devi@23 22 our %Commands_Stat; # Statistics about commands usage
devi@23 23 our %Files_Stat; # Statistics about commands usage
devi@23 24
devi@25 25 our %Script_Files; # Информация о позициях в скрипт-файлах,
devi@25 26 # до которых уже выполнен разбор
devi@25 27 # и информация о времени модификации файла
devi@25 28 # $Script_Files{$file}->{size}
devi@25 29 # $Script_Files{$file}->{tell}
devi@25 30
devi@25 31 our $Killed =0; # В режиме демона -- процесс получил сигнал о завершении
devi@23 32
devi@23 33 sub init_variables;
devi@23 34 sub main;
devi@23 35
devi@23 36 sub load_diff_files;
devi@23 37 sub bind_diff;
devi@23 38 sub extract_from_cline;
devi@23 39 sub load_command_lines;
devi@23 40 sub sort_command_lines;
devi@23 41 sub process_command_lines;
devi@23 42 sub print_command_lines;
devi@23 43 sub printq;
devi@23 44
devi@25 45 sub save_cache_stat;
devi@25 46 sub load_cache_stat;
devi@25 47
devi@25 48
devi@23 49 sub load_diff_files
devi@23 50 {
devi@23 51 my @pathes = @_;
devi@23 52
devi@23 53 for my $path (@pathes) {
devi@23 54 my $template = "*.diff";
devi@23 55 my @files = <$path/$template>;
devi@23 56 my $i=0;
devi@23 57 for my $file (@files) {
devi@23 58 my %diff;
devi@23 59
devi@23 60 $diff{"path"}=$path;
devi@23 61 $diff{"uid"}="SET THIS";
devi@23 62
devi@23 63 # Сейчас UID определяется из названия каталога
devi@23 64 # откуда берутся diff-файлы
devi@23 65 # Это неправильно
devi@23 66 #
devi@23 67 # ВАРИАНТ:
devi@23 68 # К файлам жураналам должны прилагаться ситемны файлы,
devi@23 69 # мз которых и будет определяться соответствие
devi@23 70 # имён пользователей их uid'ам
devi@23 71 #
devi@23 72 $diff{"uid"} = 0 if $path =~ m@/root/@;
devi@23 73
devi@23 74 $diff{"bind_to"}="";
devi@23 75 $diff{"time_range"}=-1;
devi@23 76
devi@23 77 next if not $file=~m@/(D?[0-9][0-9]?[0-9]?)[^/]*?([0-9]*):([0-9]*):?([0-9]*)@;
devi@23 78 $diff{"day"}=$1 || "";
devi@23 79 $diff{"hour"}=$2;
devi@23 80 $diff{"min"}=$3;
devi@23 81 $diff{"sec"}=$4 || 0;
devi@23 82
devi@23 83 $diff{"index"}=$i;
devi@23 84
devi@23 85 print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n";
devi@23 86
devi@23 87 local $/;
devi@23 88 open (F, "$file")
devi@23 89 or return "Can't open file $file ($_[0]) for reading";
devi@23 90 my $text = <F>;
devi@23 91 if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i) {
devi@23 92 my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8");
devi@23 93 $text = $converter->convert($text);
devi@23 94 }
devi@23 95 close(F);
devi@23 96 $diff{"text"}=$text;
devi@23 97 #print "$file loaded ($diff{day})\n";
devi@23 98
devi@23 99 push @Diffs, \%diff;
devi@23 100 $i++;
devi@23 101 }
devi@23 102 }
devi@23 103 }
devi@23 104
devi@23 105
devi@23 106 sub bind_diff
devi@23 107 {
devi@23 108 # my $path = shift;
devi@23 109 # my $pid = shift;
devi@23 110 # my $day = shift;
devi@23 111 # my $lab = shift;
devi@23 112
devi@23 113 print "Trying to bind diff...\n";
devi@23 114
devi@23 115 my $cl = shift;
devi@23 116 my $hour = $cl->{"hour"};
devi@23 117 my $min = $cl->{"min"};
devi@23 118 my $sec = $cl->{"sec"};
devi@23 119
devi@23 120 my $min_dt = 10000;
devi@23 121
devi@23 122 for my $diff (@Diffs) {
devi@23 123 # Check here date, time and user
devi@23 124 next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"}));
devi@23 125 #next if (!$diff->{"uid"} && $cl->{"euid"} != $diff->{"uid"});
devi@23 126
devi@23 127 my $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec);
devi@23 128 if ($dt >0 && $dt < $min_dt && ($diff->{"time_range"} <0 || $dt < $diff->{"time_range"})) {
devi@23 129 print "Approppriate diff found: dt=$dt\n";
devi@23 130 if ($diff->{"bind_to"}) {
devi@23 131 undef $diff->{"bind_to"}->{"diff"};
devi@23 132 };
devi@23 133 $diff->{"time_range"}=$dt;
devi@23 134 $diff->{"bind_to"}=$cl;
devi@23 135
devi@23 136 $cl->{"diff"} = $diff->{"index"};
devi@23 137 $min_dt = $dt;
devi@23 138 }
devi@23 139
devi@23 140 }
devi@23 141 }
devi@23 142
devi@23 143
devi@23 144 sub extract_from_cline
devi@23 145 # Разобрать командную строку $_[1] и возвратить хэш, содержащий
devi@23 146 # номер первого появление команды в строке:
devi@23 147 # команда => первая позиция
devi@23 148 {
devi@23 149 my $what = $_[0];
devi@23 150 my $cline = $_[1];
devi@23 151 my @lists = split /\;/, $cline;
devi@23 152
devi@23 153
devi@23 154 my @commands = ();
devi@23 155 for my $list (@lists) {
devi@23 156 push @commands, split /\|/, $list;
devi@23 157 }
devi@23 158
devi@23 159 my %commands;
devi@23 160 my %files;
devi@23 161 my $i=0;
devi@23 162 for my $command (@commands) {
devi@23 163 $command =~ /\s*(\S+)\s*(.*)/;
devi@23 164 if ($1 && $1 eq "sudo" ) {
devi@23 165 $commands{"$1"}=$i++;
devi@23 166 $command =~ s/\s*sudo\s+//;
devi@23 167 }
devi@23 168 $command =~ /\s*(\S+)\s*(.*)/;
devi@23 169 if ($1 && !defined $commands{"$1"}) {
devi@23 170 $commands{"$1"}=$i++;
devi@23 171 };
devi@23 172 if ($2) {
devi@23 173 my $args = $2;
devi@23 174 my @args = split (/\s+/, $args);
devi@23 175 for my $a (@args) {
devi@23 176 $files{"$a"}=$i++
devi@23 177 if !defined $files{"$a"};
devi@23 178 };
devi@23 179
devi@23 180
devi@23 181 }
devi@23 182 }
devi@23 183
devi@23 184 if ($what eq "commands") {
devi@23 185 return %commands;
devi@23 186 } else {
devi@23 187 return %files;
devi@23 188 }
devi@23 189
devi@23 190 }
devi@23 191
devi@23 192 sub load_command_lines
devi@23 193 {
devi@23 194 my $lab_scripts_path = $_[0];
devi@23 195 my $lab_scripts_mask = $_[1];
devi@23 196
devi@23 197 my $cline_re_base = qq'
devi@25 198 (
devi@25 199 (?:\\^?([0-9]*C?)) # exitcode
devi@23 200 (?:_([0-9]+)_)? # uid
devi@23 201 (?:_([0-9]+)_) # pid
devi@23 202 (...?) # day
devi@23 203 (.?.?) # lab
devi@23 204 \\s # space separator
devi@23 205 ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]) # time
devi@23 206 .\\[50D.\\[K # killing symbols
devi@23 207 (.*?([\$\#]\\s?)) # prompt
devi@23 208 (.*) # command line
devi@25 209 )
devi@23 210 ';
devi@23 211 #my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x;
devi@23 212 #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x;
devi@23 213 my $cline_re = qr/$cline_re_base/sx;
devi@23 214 my $cline_re1 = qr/$cline_re_base\x0D/sx;
devi@23 215 my $cline_re2 = qr/$cline_re_base$/sx;
devi@23 216
devi@23 217 my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"},
devi@23 218 'rows' => $Config{"terminal_height"});
devi@23 219 my $cline_vt = Term::VT102->new ('cols' => $Config{"terminal_width"},
devi@23 220 'rows' => $Config{"terminal_height"});
devi@23 221
devi@23 222 my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8")
devi@23 223 if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i);
devi@23 224
devi@23 225 print "Loading lm-scripts...\n" if $Config{"verbose"} =~ /y/;
devi@23 226
devi@23 227 my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>;
devi@23 228 my $file;
devi@23 229 my $files_number = $#lab_scripts;
devi@23 230 my $ii = 0;
devi@23 231 my $skip_info;
devi@23 232
devi@23 233 my $commandlines_loaded =0;
devi@23 234 my $commandlines_processed =0;
devi@23 235
devi@23 236 for $file (@lab_scripts){
devi@23 237 #printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/;
devi@25 238
devi@25 239 # Пропускаем файл, если он не изменялся со времени нашего предудущего прохода
devi@25 240 my $size = (stat($file))[7];
devi@25 241 next if ($Script_Files{$file} && $Script_Files{$file}->{size} && $Script_Files{$file}->{size} >= $size);
devi@23 242
devi@23 243 open (FILE, "$file");
devi@23 244 binmode FILE;
devi@25 245
devi@25 246 # Переходим к тому месту, где мы окончили разбор
devi@25 247 seek (FILE, $Script_Files{$file}->{tell}, 0) if $Script_Files{$file}->{tell};
devi@25 248 $Script_Files{$file}->{size} = $size;
devi@25 249 $Script_Files{$file}->{tell} = 0 unless $Script_Files{$file}->{tell};
devi@25 250
devi@25 251
devi@23 252 $file =~ m@.*/(.*?)-.*@;
devi@23 253
devi@23 254 my $tty = $1;
devi@23 255 my $first_pass = 1;
devi@23 256 my %cl;
devi@23 257 my $last_output_length=0;
devi@23 258 while (<FILE>) {
devi@25 259
devi@23 260 $commandlines_processed++;
devi@23 261 # time
devi@23 262
devi@23 263 if (/[0-9][0-9]:[0-9][0-9]:[0-9][0-9].\[[0-9][0-9]D.\[K/ && m/$cline_re/) {
devi@23 264 s/.*\x0d(?!\x0a)//;
devi@23 265 # print "!!!",$_,"!!!\n";
devi@23 266 # next;
devi@23 267 # while (m/$cline_re1/gs) {
devi@23 268 # }
devi@23 269 m/$cline_re2/gs;
devi@23 270
devi@23 271 $commandlines_loaded++;
devi@23 272 $last_output_length=0;
devi@23 273
devi@23 274 # Previous command
devi@23 275 my %last_cl = %cl;
devi@25 276 my $err = $2 || "";
devi@23 277
devi@23 278
devi@23 279 =cut
devi@23 280
devi@23 281 ТАБЛИЦА КОМАНД
devi@23 282
devi@23 283 uid
devi@23 284 Идентификатор пользователя
devi@23 285
devi@23 286 tty
devi@23 287 Идентификатор терминала, на котором была вызвана команда
devi@23 288
devi@23 289 pid
devi@23 290 PID-процесса командного интерпретатора,
devi@23 291 в котором была вызвана команда
devi@23 292
devi@23 293 lab
devi@23 294 лабораторная работа, к которой относится команда.
devi@23 295 Идентификатор текущей лабораторной работы
devi@23 296 хранится в файле ~/.labmaker/lab
devi@23 297
devi@23 298 pwd (!)
devi@23 299 текущий каталог, из которого была вызвана команда
devi@23 300
devi@23 301 day
devi@23 302 время вызова, день
devi@23 303 В действительности здесь хранится не время вызова команды,
devi@23 304 а с момента появления приглашения командного интерпретатора
devi@23 305 для ввода команды
devi@23 306
devi@23 307
devi@23 308 hour
devi@23 309 время вызова, час
devi@23 310
devi@23 311 min
devi@23 312 время вызова, минута
devi@23 313
devi@23 314 sec
devi@23 315 время вызова, секунда
devi@23 316
devi@23 317 time (!)
devi@23 318 время вызова команды в Unix-формате.
devi@23 319 Предпочтительнее использовать этот формат чем hour:min:sec,
devi@23 320 использовавшийся в Labmaker
devi@23 321
devi@23 322 fullprompt
devi@23 323 Приглашение командной строки
devi@23 324
devi@23 325 prompt
devi@23 326 Сокращённое приглашение командной строки
devi@23 327
devi@23 328 cline
devi@23 329 Командная строка
devi@23 330
devi@23 331 output
devi@23 332 Результат выполнения команды
devi@23 333
devi@23 334 diff
devi@23 335 Указатель на ассоциированный с командой diff
devi@23 336
devi@23 337 note (!)
devi@23 338 Текстовый комментарий к команде.
devi@23 339 Может генерироваться из самого лога с помощью команд
devi@23 340 #^ Комментарий
devi@23 341 #v Комментарий
devi@23 342 в том случае, если для комментирования достаточно одной строки,
devi@23 343 или с помощью команд
devi@23 344 cat > /dev/null #^ Заголовок
devi@23 345 Текст
devi@23 346 ^D
devi@23 347 в том случае, если комментарий развёрнутый.
devi@23 348 В последнем случае комментарий может содержать
devi@23 349 заголовок, абзацы и несложное форматирование.
devi@23 350
devi@23 351 Символ ^ или v после знака комментария # обозначает,
devi@23 352 к какой команде относится комментарий:
devi@23 353 к предыдущей (^) или последующей (v)
devi@23 354
devi@23 355 err
devi@23 356 Код завершения командной строки
devi@23 357
devi@23 358 histnum (!)
devi@23 359 Номер команды в истории командного интерпретатора
devi@23 360
devi@23 361 status (!)
devi@23 362 Является ли данная команда вызванной (r), запомненной (s)
devi@23 363 или это подсказка completion (c).
devi@23 364
devi@23 365 Команды, которые были вызваны и обработаны интерпретатором
devi@23 366 имеют состояние "r". К таким командам относится большинство
devi@23 367 команд вводимых в интерпретатор.
devi@23 368
devi@23 369 Если команда набрана, но вызывать её по какой-либо причине
devi@23 370 не хочется (например, команда может быть не полной, вредоносной
devi@23 371 или просто бессмысленной в текущих условиях),
devi@23 372 её можно сбросить с помощью комбинации клавиш Ctrl-C
devi@23 373 (не путайте с прерыванием работающей команды! здесь она даже
devi@23 374 не запускается!).
devi@23 375 В таком случае она не выполняется, но попадает в журнал
devi@23 376 со статусом "s".
devi@23 377
devi@23 378 Если команда появилась в журнале благодаря автопроолжению
devi@23 379 -- когда было показано несколько вариантов --
devi@23 380 она имеет статус "c".
devi@23 381
devi@23 382 euid
devi@23 383 Идентификатор пользователя от имени которого будет
devi@23 384 выполняться команда.
devi@23 385 Может отличаться от реального uid в том случае,
devi@23 386 если вызывается с помощью sudo
devi@23 387
devi@23 388
devi@23 389 version (!)
devi@23 390 Версия lilalo-prompt использовавшаяся при записи
devi@23 391 команды.
devi@23 392
devi@23 393 0 - версия использовавшая в labmaker.
devi@23 394 Отсутствует информация о текущем каталоге и номере в истории.
devi@23 395 Информация о версии также не указана в приглашении.
devi@23 396
devi@23 397
devi@23 398 1 - версия использующаяся в lilalo
devi@23 399
devi@23 400 raw_file (*)
devi@23 401 Имя файла, в котором находится бинарное представление журнала.
devi@23 402 Может содержать ключевое слово HERE,
devi@23 403 обозначающее что бинарное представление хранится
devi@23 404 непосредственно в базе данных в атрибуте raw_data
devi@23 405
devi@23 406 raw_start (*)
devi@23 407 Начало блока командной строки в файле бинарного представления
devi@23 408
devi@25 409 raw_output_start (*)
devi@25 410 Начало блока вывода
devi@25 411
devi@23 412 raw_end (*)
devi@23 413 Конец блока командной строки в файле бинарного представления
devi@23 414
devi@23 415 raw_cline (*)
devi@25 416 Необработанная командная строка (без приглашения) в бинарном виде
devi@23 417
devi@23 418 raw_data (*)
devi@23 419 Бинарное представление команды и результатов её выполнения
devi@23 420
devi@23 421
devi@23 422
devi@23 423
devi@23 424 ТАБЛИЦА SESSION
devi@23 425
devi@23 426 Информация о сеансах
devi@23 427
devi@23 428
devi@23 429
devi@23 430
devi@23 431 =cut
devi@23 432
devi@23 433 # Parse new command
devi@25 434 $cl{"uid"} = $3;
devi@23 435 $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0
devi@25 436 $cl{"pid"} = $4;
devi@25 437 $cl{"day"} = $5;
devi@25 438 $cl{"lab"} = $6;
devi@25 439 $cl{"hour"} = $7;
devi@25 440 $cl{"min"} = $8;
devi@25 441 $cl{"sec"} = $9;
devi@25 442 $cl{"fullprompt"} = $10;
devi@25 443 $cl{"prompt"} = $11;
devi@25 444 $cl{"raw_cline"} = $12;
devi@25 445
devi@25 446 {
devi@25 447 use bytes;
devi@25 448 $cl{"raw_start"} = tell (FILE) - length($1);
devi@25 449 $cl{"raw_output_start"} = tell FILE;
devi@25 450 }
devi@25 451 $cl{"raw_file"} = $file;
devi@23 452
devi@23 453 $cl{"err"} = 0;
devi@23 454 $cl{"output"} = "";
devi@23 455 $cl{"tty"} = $tty;
devi@23 456
devi@23 457 $cline_vt->process($cl{"raw_cline"}."\n");
devi@23 458 $cl{"cline"} = $cline_vt->row_plaintext (1);
devi@23 459 $cl{"cline"} =~ s/\s*$//;
devi@23 460 $cline_vt->reset();
devi@23 461
devi@23 462 my %commands = extract_from_cline("commands", $cl{"cline"});
devi@23 463 $cl{"euid"}=0 if defined $commands{"sudo"};
devi@23 464 my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands;
devi@23 465 $cl{"last_command"} = $comms[$#comms] || "";
devi@23 466
devi@23 467 if (
devi@23 468 $Config{"suppress_editors"} =~ /^y/i
devi@23 469 && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}}) ||
devi@23 470 $Config{"suppress_pagers"} =~ /^y/i
devi@23 471 && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}}) ||
devi@23 472 $Config{"suppress_terminal"}=~ /^y/i
devi@23 473 && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}})
devi@23 474 ) {
devi@23 475 $cl{"suppress_output"} = "1";
devi@23 476 }
devi@23 477 else {
devi@23 478 $cl{"suppress_output"} = "0";
devi@23 479
devi@23 480 }
devi@23 481 $skip_info = 0;
devi@23 482
devi@23 483
devi@23 484 print " ",$cl{"last_command"};
devi@23 485
devi@23 486 # Processing previous command line
devi@23 487 if ($first_pass) {
devi@23 488 $first_pass = 0;
devi@23 489 next;
devi@23 490 }
devi@23 491
devi@23 492 # Error code
devi@25 493 $last_cl{"raw_end"} = $cl{"raw_start"};
devi@23 494 $last_cl{"err"}=$err;
devi@23 495 $last_cl{"err"}=130 if $err eq "^C";
devi@23 496
devi@23 497 if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) {
devi@23 498 bind_diff(\%last_cl);
devi@23 499 }
devi@23 500
devi@23 501 # Output
devi@23 502 if (!$last_cl{"suppress_output"} || $last_cl{"err"}) {
devi@23 503 for (my $i=0; $i<$Config{"terminal_height"}; $i++) {
devi@23 504 my $line= $vt->row_plaintext($i);
devi@23 505 next if !defined ($line) || $line =~ /^\s*$/;
devi@23 506 $line =~ s/\s*$//;
devi@23 507 $last_cl{"output"} .= $line."\n";
devi@23 508 }
devi@23 509 }
devi@23 510 else {
devi@23 511 $last_cl{"output"}= "";
devi@23 512 }
devi@23 513
devi@23 514 $vt->reset();
devi@23 515
devi@23 516
devi@23 517 # Classifying the command line
devi@23 518
devi@23 519
devi@23 520 # Save
devi@23 521 if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) {
devi@23 522 # Changing encoding
devi@23 523 for (keys %last_cl) {
devi@25 524 next if /raw/;
devi@23 525 $last_cl{$_} = $converter->convert($last_cl{$_})
devi@23 526 if ($Config{"encoding"} &&
devi@23 527 $Config{"encoding"} !~ /^utf-8$/i);
devi@23 528 }
devi@23 529 push @Command_Lines, \%last_cl;
devi@25 530
devi@25 531 # Сохранение позиции в файле, до которой выполнен
devi@25 532 # успешный разбор
devi@25 533 $Script_Files{$file}->{tell} = $last_cl{raw_end};
devi@23 534 }
devi@23 535 next;
devi@23 536 }
devi@23 537 $last_output_length+=length($_);
devi@23 538 #if (!$cl{"suppress_output"} || $last_output_length < 5000) {
devi@23 539 if ($last_output_length < 50000) {
devi@23 540 #print "(",length($_),")" if (length($_) > 2000) ;
devi@23 541 $vt->process("$_"."\n")
devi@23 542 }
devi@23 543 else
devi@23 544 {
devi@23 545 if (!$skip_info) {
devi@23 546 print "($cl{last_command})";
devi@23 547 $skip_info = 1;
devi@23 548 }
devi@23 549 }
devi@23 550 }
devi@23 551 close(FILE);
devi@23 552
devi@23 553 }
devi@23 554 if ($Config{"verbose"} =~ /y/) {
devi@23 555 print "...finished." ;
devi@23 556 print "Lines loaded: $commandlines_processed\n";
devi@23 557 print "Command lines: $commandlines_loaded\n";
devi@23 558 }
devi@23 559 }
devi@23 560
devi@23 561
devi@23 562
devi@23 563 sub printq
devi@23 564 {
devi@23 565 my $TO = shift;
devi@23 566 my $text = join "", @_;
devi@23 567 $text =~ s/&/&amp;/g;
devi@23 568 $text =~ s/</&lt;/g;
devi@23 569 $text =~ s/>/&gt;/g;
devi@23 570 print $TO $text;
devi@23 571 }
devi@23 572
devi@23 573
devi@23 574 sub sort_command_lines
devi@23 575 {
devi@23 576 print "Sorting command lines...\n" if $Config{"verbose"} =~ /y/;
devi@23 577
devi@23 578 # Sort Command_Lines
devi@23 579 # Write Command_Lines to Command_Lines_Index
devi@23 580
devi@23 581 my @index;
devi@23 582 for (my $i=0;$i<=$#Command_Lines;$i++) {
devi@23 583 $index[$i]=$i;
devi@23 584 }
devi@23 585
devi@23 586 @Command_Lines_Index = sort {
devi@23 587 $Command_Lines[$index[$a]]->{"day"} cmp $Command_Lines[$index[$b]]->{"day"} ||
devi@23 588 $Command_Lines[$index[$a]]->{"hour"} <=> $Command_Lines[$index[$b]]->{"hour"} ||
devi@23 589 $Command_Lines[$index[$a]]->{"min"} <=> $Command_Lines[$index[$b]]->{"min"} ||
devi@23 590 $Command_Lines[$index[$a]]->{"sec"} <=> $Command_Lines[$index[$b]]->{"sec"}
devi@23 591 } @index;
devi@23 592
devi@23 593 print "...finished\n" if $Config{"verbose"} =~ /y/;
devi@23 594
devi@23 595 }
devi@23 596
devi@23 597 sub process_command_lines
devi@23 598 {
devi@23 599 for my $i (@Command_Lines_Index) {
devi@23 600
devi@23 601 my $cl = \$Command_Lines[$i];
devi@23 602 @{${$cl}->{"new_commands"}} =();
devi@23 603 @{${$cl}->{"new_files"}} =();
devi@23 604 $$cl->{"class"} = "";
devi@23 605
devi@23 606 if ($$cl->{"err"}) {
devi@23 607 $$cl->{"class"}="wrong";
devi@23 608 $$cl->{"class"}="interrupted"
devi@23 609 if ($$cl->{"err"} eq 130);
devi@23 610 }
devi@23 611 if (!$$cl->{"euid"}) {
devi@23 612 $$cl->{"class"}.="_root";
devi@23 613 }
devi@23 614
devi@23 615 #tab# my @tab_words=split /\s+/, $$cl->{"output"};
devi@23 616 #tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/;
devi@23 617 #tab# $last_word =~ s@.*/@@;
devi@23 618 #tab# my $this_is_tab=1;
devi@23 619 #tab#
devi@23 620 #tab# if ($last_word && @tab_words >2) {
devi@23 621 #tab# for my $tab_words (@tab_words) {
devi@23 622 #tab# if ($tab_words !~ /^$last_word/) {
devi@23 623 #tab# $this_is_tab=0;
devi@23 624 #tab# last;
devi@23 625 #tab# }
devi@23 626 #tab# }
devi@23 627 #tab# }
devi@23 628 #tab# $$cl->{"class"}="tab" if $this_is_tab;
devi@23 629
devi@23 630
devi@23 631 if ( !$$cl->{"err"}) {
devi@23 632 # Command does not contain mistakes
devi@23 633
devi@23 634 my %commands = extract_from_cline("commands", ${$cl}->{"cline"});
devi@23 635 my %files = extract_from_cline("files", ${$cl}->{"cline"});
devi@23 636
devi@23 637 # Searching for new commands only
devi@23 638 for my $command (keys %commands) {
devi@23 639 if (!defined $Commands_Stat{$command}) {
devi@23 640 push @{$$cl->{new_commands}}, $command;
devi@23 641 }
devi@23 642 $Commands_Stat{$command}++;
devi@23 643 }
devi@23 644
devi@23 645 for my $file (keys %files) {
devi@23 646 if (!defined $Files_Stat{$file}) {
devi@23 647 push @{$$cl->{new_files}}, $file;
devi@23 648 }
devi@23 649 $Files_Stat{$file}++;
devi@23 650 }
devi@23 651 }
devi@23 652 }
devi@23 653
devi@23 654 }
devi@23 655
devi@23 656
devi@23 657 =cut
devi@23 658 Вывести результат обработки журнала.
devi@23 659 =cut
devi@23 660
devi@23 661
devi@23 662 sub print_command_lines
devi@23 663 {
devi@23 664 my $output_filename=$_[0];
devi@25 665 my $mode = ">";
devi@25 666 $mode =">>" if $Config{mode} eq "daemon";
devi@25 667 open(OUT, $mode, $output_filename)
devi@23 668 or die "Can't open $output_filename for writing\n";
devi@23 669
devi@23 670
devi@23 671
devi@25 672 #print OUT "<livelablog>\n";
devi@23 673
devi@23 674 my $cl;
devi@23 675 my $in_range=0;
devi@23 676 for my $i (@Command_Lines_Index) {
devi@23 677 $cl = $Command_Lines[$i];
devi@23 678
devi@23 679 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
devi@23 680 $in_range=1;
devi@23 681 next;
devi@23 682 }
devi@23 683 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
devi@23 684 $in_range=0;
devi@23 685 next;
devi@23 686 }
devi@23 687 next if ($Config{"from"} && $Config{"to"} && !$in_range)
devi@23 688 ||
devi@23 689 ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
devi@23 690 ||
devi@23 691 ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
devi@23 692 ||
devi@23 693 ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
devi@23 694
devi@23 695 my @new_commands=@{$cl->{"new_commands"}};
devi@23 696 my @new_files=@{$cl->{"new_files"}};
devi@23 697
devi@23 698 my $cl_class="cline";
devi@23 699 my $out_class="output";
devi@23 700 if ($cl->{"class"}) {
devi@23 701 $cl_class = $cl->{"class"}."_".$cl_class;
devi@23 702 $out_class = $cl->{"class"}."_".$out_class;
devi@23 703 }
devi@23 704
devi@23 705 # Вырезаем из вывода только нужное количество строк
devi@23 706
devi@23 707 my $output="";
devi@23 708 if ($Config{"head_lines"} || $Config{"tail_lines"}) {
devi@23 709 # Partialy output
devi@23 710 my @lines = split '\n', $cl->{"output"};
devi@23 711 # head
devi@23 712 my $mark=1;
devi@23 713 for (my $i=0; $i<= $#lines && $i < $Config{"cache_head_lines"}; $i++) {
devi@23 714 $output .= $lines[$i]."\n";
devi@23 715 }
devi@23 716 # tail
devi@23 717 my $start=$#lines-$Config{"cache_tail_lines"}+1;
devi@23 718 if ($start < 0) {
devi@23 719 $start=0;
devi@23 720 $mark=0;
devi@23 721 }
devi@23 722 if ($start < $Config{"cache_head_lines"}) {
devi@23 723 $start=$Config{"cache_head_lines"};
devi@23 724 $mark=0;
devi@23 725 }
devi@23 726 $output .= $Config{"skip_text"}."\n" if $mark;
devi@23 727 for (my $i=$start; $i<= $#lines; $i++) {
devi@23 728 $output .= $lines[$i]."\n";
devi@23 729 }
devi@23 730 }
devi@23 731 else {
devi@23 732 # Full output
devi@23 733 $output .= $cl->{"output"};
devi@23 734 }
devi@23 735 $output .= "^C\n" if ($cl->{"err"} eq "130");
devi@23 736
devi@23 737
devi@23 738 # Совместимость с labmaker
devi@23 739
devi@23 740 # Переводим в секунды Эпохи
devi@23 741 # В labmaker'е данные хранились в неудобной форме: hour, min, sec, day of year
devi@23 742 # Информация о годе отсутствовала
devi@23 743 # Её можно внести:
devi@23 744 # Декабрь 2004 год; остальные -- 2005 год.
devi@23 745
devi@23 746 my $year = 2005;
devi@23 747 $year = 2004 if ( $cl->{day} > 330 );
devi@23 748 # timelocal( $sec, $min, $hour, $mday,$mon,$year);
devi@23 749 $cl->{time} = timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year);
devi@23 750
devi@23 751
devi@23 752 # Начинаем вывод команды
devi@23 753 print OUT "<command>\n";
devi@23 754 print OUT "<time>",$cl->{time},"</time>\n";
devi@25 755 print OUT "<raw_start>",$cl->{raw_start},"</raw_start>\n";
devi@25 756 print OUT "<raw_output_start>",$cl->{raw_output_start},"</raw_output_start>\n";
devi@25 757 print OUT "<raw_end>",$cl->{raw_end},"</raw_end>\n";
devi@25 758 print OUT "<raw_file>",$cl->{raw_file},"</raw_file>\n";
devi@23 759 print OUT "<tty>",$cl->{tty},"</tty>\n";
devi@23 760 print OUT "<out_class>",$out_class,"</out_class>\n";
devi@23 761 print OUT "<prompt>";
devi@23 762 printq(\*OUT,,$cl->{"prompt"});
devi@23 763 print OUT "</prompt>";
devi@23 764 print OUT "<cline>";
devi@23 765 printq(\*OUT,$cl->{"cline"});
devi@23 766 print OUT "</cline>\n";
devi@23 767 print OUT "<last_command>",$cl->{"last_command"},"</last_command>\n";
devi@23 768 if (@new_commands) {
devi@23 769 print OUT "<new_commands>";
devi@23 770 printq(\*OUT, join (" ", @new_commands));
devi@23 771 print OUT "</new_commands>";
devi@23 772 }
devi@23 773 if (@new_files) {
devi@23 774 print OUT "<new_files>";
devi@23 775 printq(\*OUT, join (" ", @new_files));
devi@23 776 print OUT "</new_files>";
devi@23 777 }
devi@23 778 print OUT "<output>";
devi@23 779 printq(\*OUT,$output);
devi@23 780 print OUT "</output>\n";
devi@23 781 if ($cl->{"diff"}) {
devi@23 782 print OUT "<diff>";
devi@23 783 printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"});
devi@23 784 print OUT "</diff>\n";
devi@23 785 }
devi@23 786 print OUT "</command>\n";
devi@23 787
devi@23 788 }
devi@23 789
devi@25 790 #print OUT "</livelablog>\n";
devi@23 791 close(OUT);
devi@25 792 save_cache_stat();
devi@23 793 }
devi@23 794
devi@25 795 sub save_cache_stat
devi@25 796 {
devi@25 797 open (CACHE, ">$Config{cache_stat}");
devi@25 798 for my $f (keys %Script_Files) {
devi@25 799 print CACHE "$f\t",$Script_Files{$f}->{size},"\t",$Script_Files{$f}->{tell},"\n";
devi@25 800 }
devi@25 801 close(CACHE);
devi@25 802 }
devi@25 803
devi@25 804 sub load_cache_stat
devi@25 805 {
devi@25 806 if (open (CACHE, "$Config{cache_stat}")) {
devi@25 807 while(<CACHE>) {
devi@25 808 my ($f, $size, $tell) = split /\t/;
devi@25 809 $Script_Files{$f}->{size} = $size;
devi@25 810 $Script_Files{$f}->{tell} = $tell;
devi@25 811 }
devi@25 812 close(CACHE);
devi@25 813 };
devi@25 814 }
devi@23 815
devi@23 816 =cut
devi@23 817 sub print_command_lines2
devi@23 818 {
devi@23 819 my $output_filename=$_[0];
devi@23 820 open(OUT, ">", $output_filename)
devi@23 821 or die "Can't open $output_filename for writing\n";
devi@23 822
devi@23 823
devi@23 824 print OUT <<OUT;
devi@23 825 <log>
devi@23 826 OUT
devi@23 827
devi@23 828 my $cl;
devi@23 829 for my $i (@Command_Lines_Index) {
devi@23 830
devi@23 831
devi@23 832 $cl = $Command_Lines[$i];
devi@23 833
devi@23 834
devi@23 835 # Printing out
devi@23 836 print OUT <<OUT;
devi@23 837 <command>
devi@23 838 <day>$cl->{day}</day>
devi@23 839 <hour>$cl->{hour}</hour>
devi@23 840 <min>$cl->{min}</min>
devi@23 841 <sec>$cl->{sec}</sec>
devi@23 842 <tty>$cl->{tty}</tty>
devi@23 843 <uid>$cl->{uid}</uid>
devi@23 844 <euid>$cl->{euid}</euid>
devi@23 845 <prompt>$cl->{prompt}</prompt>
devi@23 846 <cline>$cl->{cline}</cline>
devi@23 847 <status>$cl->{err}</cline>
devi@23 848 <output>
devi@23 849 $cl->{output}</output>
devi@23 850 </command>
devi@23 851 OUT
devi@23 852 }
devi@23 853
devi@23 854 for my $diff (@Diffs) {
devi@23 855
devi@23 856 print OUT <<OUT;
devi@23 857 <diff>
devi@23 858 <path>$diff->{path}</path>
devi@23 859 <uid>$diff->{uid}</uid>
devi@23 860 <day>$diff->{day}</day>
devi@23 861 <hour>$diff->{hour}</hour>
devi@23 862 <min>$diff->{min}</min>
devi@23 863 <sec>$diff->{sec}</sec>
devi@23 864 <text>
devi@23 865 $diff->{text}</text>
devi@23 866 </diff>
devi@23 867 OUT
devi@23 868 }
devi@23 869
devi@23 870 print OUT <<OUT;
devi@23 871 </log>
devi@23 872 OUT
devi@23 873 }
devi@23 874 =cut
devi@23 875
devi@23 876 main();
devi@23 877
devi@25 878 sub process_was_killed
devi@25 879 {
devi@25 880 $Killed = 1;
devi@25 881 }
devi@25 882
devi@23 883 sub main
devi@23 884 {
devi@23 885 $| = 1;
devi@23 886
devi@23 887 init_variables();
devi@23 888 init_config();
devi@23 889
devi@23 890 for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) {
devi@23 891 load_diff_files($lab_log);
devi@23 892 }
devi@23 893
devi@25 894 if ($Config{"mode"} ne "daemon") {
devi@25 895 load_command_lines($Config{"input"}, $Config{"input_mask"});
devi@25 896 sort_command_lines;
devi@25 897 process_command_lines;
devi@25 898 print_command_lines($Config{"cache"});
devi@25 899 }
devi@25 900 else {
devi@25 901 if (open(PIDFILE, $Config{agent_pidfile})) {
devi@25 902 my $pid = <PIDFILE>;
devi@25 903 close(PIDFILE);
devi@25 904 if ( ! -e "/proc/$pid" || !`grep $Config{"l3-agent"} /proc/$pid/cmdline && grep "uid:.*\b$<\b" /proc/$pid/status`) {
devi@25 905 print "Removing stale pidfile\n";
devi@25 906 unlink $Config{agent_pidfile};
devi@25 907 or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!";
devi@25 908 }
devi@25 909 else {
devi@25 910 print "l3-agent is already running\n";
devi@25 911 exit(0);
devi@25 912 }
devi@25 913 }
devi@25 914 if ($Config{detach} =~ /^y/i) {
devi@25 915 #$Config{verbose} = "no";
devi@25 916 my $pid = fork;
devi@25 917 exit if $pid;
devi@25 918 die "Couldn't fork: $!" unless defined ($pid);
devi@25 919
devi@25 920 open(PIDFILE, ">", $Config{agent_pidfile})
devi@25 921 or die "Can't open pidfile ". $Config{agent_pidfile}. " for wrting: $!";
devi@25 922 print PIDFILE $$;
devi@25 923 close(PIDFILE);
devi@25 924
devi@25 925 for my $handle (*STDIN, *STDOUT, *STDERR) {
devi@25 926 open ($handle, "+<", "/dev/null")
devi@25 927 or die "can't reopen $handle to /dev/null: $!"
devi@25 928 }
devi@25 929
devi@25 930 POSIX::setsid()
devi@25 931 or die "Can't start a new session: $!";
devi@25 932
devi@25 933 $0 = $Config{"l3-agent"};
devi@25 934
devi@25 935 $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&process_was_killed;
devi@25 936 }
devi@25 937 while (not $Killed) {
devi@25 938 @Command_Lines = ();
devi@25 939 @Command_Lines_Index = ();
devi@25 940 load_cache_stat();
devi@25 941 load_command_lines($Config{"input"}, $Config{"input_mask"});
devi@25 942 if (@Command_Lines) {
devi@25 943 sort_command_lines;
devi@25 944 process_command_lines;
devi@25 945 print_command_lines($Config{"cache"});
devi@25 946 }
devi@25 947 sleep($Config{"daemon_sleep_interval"} || 1);
devi@25 948 }
devi@25 949
devi@25 950 unlink $Config{agent_pidfile};
devi@25 951 }
devi@23 952
devi@23 953 }
devi@23 954
devi@23 955 sub init_variables
devi@23 956 {
devi@23 957 }