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

Содержание

Журнал

Вторник (09/12/06)

/dev/pts/255
09:18:31
#which screeb
which: no screeb in (/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin)
09:18:35
#which screen
/usr/bin/screen
09:18:35
#cd /usr/bin/

09:18:38
#chmod +x script

/dev/pts/5
09:18:54
$w
 11:18:55 up 3 days, 19:35,  3 users,  load average: 0,39, 0,62, 0,55
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
su       pts/2     11:14    3:26   0.03s  0.03s script -f -c bash -q /home/su/.lilalo/
su       pts/255   10:09   13.00s  0.08s  0.02s sshd: su [priv]
su       pts/5     11:18    0.00s  0.02s  0.02s script -f -c bash -q /home/su/.lilalo/

Пятница (09/09/11)

/dev/pts/0
11:22:10
$w
 13:22:12 up 21:38,  1 user,  load average: 0,70, 0,42, 0,37
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
su       pts/0     13:22    0.00s  0.02s  0.02s script -f -c bash -q /home/su/.lilalo/
11:22:12
$w
 13:22:16 up 21:38,  1 user,  load average: 0,65, 0,41, 0,37
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
su       pts/0     13:22    0.00s  0.02s  0.02s script -f -c bash -q /home/su/.lilalo/
11:22:16
$l3 pwd
/users/su/mrg/mrgar.mrg.com/su
11:23:21
$sudo su

/dev/pts/1
11:24:43
#w
 13:24:44 up 21:41,  1 user,  load average: 0.23, 0.36, 0.35
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
su       pts/0     13:22    0.00s  0.02s  0.02s script -f -c bash -q /home/su/.lilalo/
/dev/pts/0
11:24:43
#w
 13:24:44 up 21:41,  1 user,  load average: 0.23, 0.36, 0.35
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
su       pts/0     13:22    0.00s  0.02s  0.02s script -f -c bash -q /home/su/.lilalo/
/dev/pts/1
11:24:44
#cd /root/

/dev/pts/0
11:24:44
#cd /root/

/dev/pts/1
11:24:59
#l
total 510
-rw-r--r--  1 root root     564 Sep  9 13:24 .monit.state
-rw-r--r--  1 root root      38 Sep  9 13:15 .l3rc
drwxr-xr-x  2 root root     272 Sep  9 11:38 .lilalo/
-rw-------  1 root root    3450 Sep  9 11:37 .bash_history
drwx------ 16 root root    1120 Sep  9 11:37 ./
-rw-------  1 root root     563 Sep  9 11:37 .bashrc
-rw-------  1 root root     943 Sep  9 11:37 .viminfo
-rw-r--r--  1 root root     116 Sep  9 11:00 .bash_profile
drwx------  2 root root     144 Sep  9 10:57 tmp/
...
drwx------  2 root root     144 Feb 11  2008 .kde/
drwxr-xr-x  2 root root      80 Feb  5  2008 .kbd/
drwx------  2 root root     168 Feb  5  2008 .install-log/
-rw-r--r--  1 root root    1973 Feb  5  2008 autoinstall.scm
-rw-------  1 root root      24 Jan  9  2008 .bash_logout
-rw-------  1 root root     218 Jan  9  2008 .cshrc
-rw-------  1 root root     240 Jan  9  2008 .i18n
-rw-------  1 root root      45 Jan  9  2008 .rpmmacros
-rw-------  1 root root     174 Jan  9  2008 .tcshrc
-rw-------  1 root root     598 Jan  9  2008 .zshrc
/dev/pts/0
11:24:59
#l
total 510
-rw-r--r--  1 root root     564 Sep  9 13:24 .monit.state
-rw-r--r--  1 root root      38 Sep  9 13:15 .l3rc
drwxr-xr-x  2 root root     272 Sep  9 11:38 .lilalo/
-rw-------  1 root root    3450 Sep  9 11:37 .bash_history
drwx------ 16 root root    1120 Sep  9 11:37 ./
-rw-------  1 root root     563 Sep  9 11:37 .bashrc
-rw-------  1 root root     943 Sep  9 11:37 .viminfo
-rw-r--r--  1 root root     116 Sep  9 11:00 .bash_profile
drwx------  2 root root     144 Sep  9 10:57 tmp/
...
drwx------  2 root root     144 Feb 11  2008 .kde/
drwxr-xr-x  2 root root      80 Feb  5  2008 .kbd/
drwx------  2 root root     168 Feb  5  2008 .install-log/
-rw-r--r--  1 root root    1973 Feb  5  2008 autoinstall.scm
-rw-------  1 root root      24 Jan  9  2008 .bash_logout
-rw-------  1 root root     218 Jan  9  2008 .cshrc
-rw-------  1 root root     240 Jan  9  2008 .i18n
-rw-------  1 root root      45 Jan  9  2008 .rpmmacros
-rw-------  1 root root     174 Jan  9  2008 .tcshrc
-rw-------  1 root root     598 Jan  9  2008 .zshrc
/dev/pts/1
11:24:59
#cat .bashrc
# .bashrc
# User specific aliases and functions
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
alias d='ls'
alias s='cd ..'
alias p='cd -'
# Read /etc/inputrc if the variable is not defined.
[ -n "$INPUTRC" ] || export INPUTRC=/etc/inputrc
# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi
PATH=/root/bin:/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin
ENV=$HOME/.bashrc
USERNAME="root"
export USERNAME ENV PATH
#[ $0 == l3script ] && . /root/.lilalo/l3bashrc && _l3_start
. ${user_home}/.lilalo/l3bashrc && _l3_start
/dev/pts/0
11:24:59
#cat .bashrc
# .bashrc
# User specific aliases and functions
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
alias d='ls'
alias s='cd ..'
alias p='cd -'
# Read /etc/inputrc if the variable is not defined.
[ -n "$INPUTRC" ] || export INPUTRC=/etc/inputrc
# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi
PATH=/root/bin:/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin
ENV=$HOME/.bashrc
USERNAME="root"
export USERNAME ENV PATH
#[ $0 == l3script ] && . /root/.lilalo/l3bashrc && _l3_start
. ${user_home}/.lilalo/l3bashrc && _l3_start
/dev/pts/1
11:25:04
#vim .bashrc
--- /tmp/l3-saved-11499.5991.12403	2011-09-09 13:25:15 +0400
+++ .bashrc	2011-09-09 13:25:21 +0400
@@ -21,4 +21,4 @@
 USERNAME="root"
 export USERNAME ENV PATH
 #[ $0 == l3script ] && . /root/.lilalo/l3bashrc && _l3_start
-. ${user_home}/.lilalo/l3bashrc && _l3_start
+#. ${user_home}/.lilalo/l3bashrc && _l3_start
/dev/pts/0
11:25:04
#vim .bashrc
/dev/pts/1
11:25:21
#ps fax | grep script
11156 pts/0    Ss+    0:00          \_ script -f -c bash -q /home/su/.lilalo//326433028
11232 pts/0    S+     0:00              \_ script -f -c bash -q /home/su/.lilalo//32643
11467 pts/1    S+     0:00                          \_ script -f -c bash -q /home/su/.l
11498 pts/1    S+     0:00                              \_ script -f -c bash -q /home/s
11703 pts/2    S+     0:00                                      \_ grep script
/dev/pts/0
11:25:21
#ps fax | grep script
11156 pts/0    Ss+    0:00          \_ script -f -c bash -q /home/su/.lilalo//326433028
11232 pts/0    S+     0:00              \_ script -f -c bash -q /home/su/.lilalo//32643
11467 pts/1    S+     0:00                          \_ script -f -c bash -q /home/su/.l
11498 pts/1    S+     0:00                              \_ script -f -c bash -q /home/s
11703 pts/2    S+     0:00                                      \_ grep script
/dev/pts/1
11:25:54
#killall scripy
scripy: no process killed
/dev/pts/0
11:25:54
#killall scripy
scripy: no process killed
/dev/pts/0
11:26:27
$killall l3-agent

11:26:37
$killall l3-agent
l3-agent: не завершён ни один процесс
11:26:38
$cd .l
.l3rc       .lesshst    .lftp/      .lilalo/    .lpoptions
11:26:38
$cd .lilalo/

11:26:47
$l;
итого 122
-rw-r--r--  1 su   su     664 Сен  9 13:26 268158378436214913-1315560387.info
-rw-r--r--  1 su   su    1165 Сен  9 13:26 268158378436214913-1315560387.script
drwxr-xr-x  2 su   root   576 Сен  9 13:26 ./
-rw-r--r--  1 su   su     194 Сен  9 13:26 .report.dat
-rw-r--r--  1 su   su   18763 Сен  9 13:26 report.xml
-rw-r--r--  1 root root 21573 Сен  9 13:26 2304020058671131785-1315560283.info
-rw-r--r--  1 root root 10326 Сен  9 13:26 2304020058671131785-1315560283.script
-rw-r--r--  1 su   su   24352 Сен  9 13:26 3264330288285888354-1315560130.info
-rw-r--r--  1 su   su   11507 Сен  9 13:26 3264330288285888354-1315560130.script
drwx------ 30 su   su    1464 Сен  9 13:25 ../
-rw-r--r--  1 root root   315 Сен  9 13:25 2304020058671131785-1315560283_1315560315_root_.bashrc.diff
-rwxr-xr-x  1 su   root  7709 Сен  9 13:15 l3bashrc
-rwxr-xr-x  1 su   root   234 Сен  9 13:15 l3prompt
11:26:47
$tail ../.bashrc
export GREP_OPTIONS="--color=auto"
# Source global definitions
if [ -r /etc/bashrc ]; then
        . /etc/bashrc
fi
#[ $0 == l3script ] && . /home/su/.lilalo/l3bashrc && _l3_start
#. ${user_home}/.lilalo/l3bashrc && _l3_start
. ~/.lilalo/l3bashrc && _l3_start
11:27:09
$sudo vim ../../kos_aa/.bashrc
11:27:46
$l
итого 126
-rw-r--r--  1 su   su    7908 Сен  9 13:27 268158378436214913-1315560387.script
drwx------ 30 su   su    1464 Сен  9 13:27 ../
-rw-r--r--  1 su   su     849 Сен  9 13:27 268158378436214913-1315560387.info
drwxr-xr-x  2 su   root   576 Сен  9 13:26 ./
-rw-r--r--  1 su   su     194 Сен  9 13:26 .report.dat
-rw-r--r--  1 su   su   18763 Сен  9 13:26 report.xml
-rw-r--r--  1 root root 21573 Сен  9 13:26 2304020058671131785-1315560283.info
-rw-r--r--  1 root root 10326 Сен  9 13:26 2304020058671131785-1315560283.script
-rw-r--r--  1 su   su   24352 Сен  9 13:26 3264330288285888354-1315560130.info
-rw-r--r--  1 su   su   11507 Сен  9 13:26 3264330288285888354-1315560130.script
-rw-r--r--  1 root root   315 Сен  9 13:25 2304020058671131785-1315560283_1315560315_root_.bashrc.diff
-rwxr-xr-x  1 su   root  7709 Сен  9 13:15 l3bashrc
-rwxr-xr-x  1 su   root   234 Сен  9 13:15 l3prompt
11:27:51
$cd /tmp/

11:31:33
$wget http://xgu.ru/lilalo/lilalo.tar.gz
--2011-09-09 13:31:34--  http://xgu.ru/lilalo/lilalo.tar.gz
Распознаётся xgu.ru... 91.205.16.235
Устанавливается соединение с xgu.ru|91.205.16.235|:80... соединение установлено.
Запрос HTTP послан, ожидается ответ... 200 OK
Длина: 151793 (148K) [application/x-gzip]
Saving to: «lilalo.tar.gz»
100%[=============================================>] 151 793      270K/s   в 0,5s
2011-09-09 13:31:35 (270 KB/s) - «lilalo.tar.gz» saved [151793/151793]
11:31:35
$tar -xvf lilalo.tar.gz -z
lilalo/
lilalo/l3config.pm
lilalo/lm-ssh
lilalo/lm
lilalo/tail-backend.pl
lilalo/PM/
lilalo/PM/Text-Iconv-1.4.tar.gz
lilalo/PM/Term-VT102-0.82.tar.gz
lilalo/FILES
lilalo/l3prompt
...
lilalo/CVS/
lilalo/CVS/Repository
lilalo/CVS/Root
lilalo/CVS/Entries
lilalo/l3-cgi
lilalo/lilalo.kdi
lilalo/HISTORY
lilalo/LICENSE
lilalo/l3-backend
lilalo/l3-report
11:31:38
$cd lilalo

11:31:40
$l
итого 428
drwxrwxrwt 13 root root   640 Сен  9 13:31 ../
drwxr-xr-x  8 su   su     860 Мар 16  2008 ./
-rwxr-xr-x  1 su   su   32757 Мар 16  2008 l3-agent
drwxr-xr-x  2 su   su     100 Мар 13  2008 CVS/
-rwxr-xr-x  1 su   su    7522 Мар 13  2008 l3bashrc
-rwxr-xr-x  1 su   su   78084 Мар 13  2008 l3-frontend
-rw-r--r--  1 su   su    5781 Мар 10  2008 l3config.pm
-rwxr-xr-x  1 su   su    5168 Мар 10  2008 install
-rwxr-xr-x  1 su   su     234 Мар 10  2008 l3prompt
...
-rw-r--r--  1 su   su    1598 Ноя  3  2005 tail-all.pl
-rwxr-xr-x  1 su   su      47 Ноя  2  2005 l3-report
-rwxr-xr-x  1 su   su   31413 Ноя  2  2005 lm-report
-rw-r--r--  1 su   su    4162 Окт 22  2005 lilalo.kdi
drwxr-xr-x  3 su   su     160 Май 22  2005 share/
-rw-r--r--  1 su   su    1496 Май 22  2005 FILES
-rw-r--r--  1 su   su     452 Май 22  2005 INSTALL
-rwxr-xr-x  1 su   su     316 Май 22  2005 lm-ssh
-rw-r--r--  1 su   su     157 Май 22  2005 taillast.pl
-rw-r--r--  1 su   su     111 Май 22  2005 .tarball
11:31:40
$l3
l3                 l3_close_session   l3mass_upload      l3script
l3-agent           l3-config          l3pwd              l3shot
l3cd               l3_fix_prompt      l3_save_last_line  l3upload
11:31:40
$l3-agent

11:31:52
$l3-agent
l3-agent is already running: pid=12491; pidfile=/home/su/.lilalo/l3-agent.pid
11:31:54
$cat /home/su/.bashrc
# .bashrc
# User specific aliases and functions
export GREP_OPTIONS="--color=auto"
# Source global definitions
if [ -r /etc/bashrc ]; then
        . /etc/bashrc
fi
#[ $0 == l3script ] && . /home/su/.lilalo/l3bashrc && _l3_start
#. ${user_home}/.lilalo/l3bashrc && _l3_start
. ~/.lilalo/l3bashrc && _l3_start
11:32:07
$l3 pwd
/users/su/mrg/mrgar.mrg.com/su
прошло 32 минуты
12:04:45
$l
итого 428
drwxrwxrwt 13 root root   640 Сен  9 14:06 ../
drwxr-xr-x  8 su   su     860 Мар 16  2008 ./
-rwxr-xr-x  1 su   su   32757 Мар 16  2008 l3-agent
drwxr-xr-x  2 su   su     100 Мар 13  2008 CVS/
-rwxr-xr-x  1 su   su    7522 Мар 13  2008 l3bashrc
-rwxr-xr-x  1 su   su   78084 Мар 13  2008 l3-frontend
-rw-r--r--  1 su   su    5781 Мар 10  2008 l3config.pm
-rwxr-xr-x  1 su   su    5168 Мар 10  2008 install
-rwxr-xr-x  1 su   su     234 Мар 10  2008 l3prompt
...
-rw-r--r--  1 su   su    1598 Ноя  3  2005 tail-all.pl
-rwxr-xr-x  1 su   su      47 Ноя  2  2005 l3-report
-rwxr-xr-x  1 su   su   31413 Ноя  2  2005 lm-report
-rw-r--r--  1 su   su    4162 Окт 22  2005 lilalo.kdi
drwxr-xr-x  3 su   su     160 Май 22  2005 share/
-rw-r--r--  1 su   su    1496 Май 22  2005 FILES
-rw-r--r--  1 su   su     452 Май 22  2005 INSTALL
-rwxr-xr-x  1 su   su     316 Май 22  2005 lm-ssh
-rw-r--r--  1 su   su     157 Май 22  2005 taillast.pl
-rw-r--r--  1 su   su     111 Май 22  2005 .tarball
12:06:43
$cat l3
l3-agent     l3-cgi       l3config.pm  l3prompt     l3-upload
l3-backend   l3-cgi-lite  l3files/     l3-report
l3bashrc     l3-config    l3-frontend  l3scripts
12:06:43
$cat l3-backend
#!/usr/bin/perl
use strict;
use lib '/etc/lilalo/';
use l3config;
use IO::Socket;
use POSIX qw(:sys_wait_h);
sub main;
main();
sub REAPER {
        1 until (-1 == waitpid(-1, WNOHANG));
...
                    };
                }
            }
        }
        }
        continue {
                # Наш родитель
                close ($client);
        }
}
12:07:11
$cd
CVS/                l3-cgi              l3-upload           README
DELETE/             l3-cgi-lite         LICENSE             rebuild-all-logs
doc/                l3-config           lilalo.kdi          share/
FILES               l3config.pm         lm                  tail-all.pl
HISTORY             .#l3config.pm.1.15  .#lm.1.14           tail-backend.pl
install             l3files/            lm-install          taillast.pl
INSTALL             l3-frontend         lm-report           .tarball
l3-agent            .#l3-frontend.1.26  lm-ssh              TODO
.#l3-agent.1.21     l3prompt            mysql
l3-backend          l3-report           nohup.out
l3bashrc            l3scripts           PM/
12:07:11
$l /etc/init.d/l
linbilling-timer  local             lvm2-monitor
12:07:11
$l3
l3                 l3_close_session   l3mass_upload      l3script
l3-agent           l3-config          l3pwd              l3shot
l3cd               l3_fix_prompt      l3_save_last_line  l3upload
12:07:11
$cp l3-backend /usr/bin/
cp: невозможно создать обычный файл `/usr/bin/l3-backend': Отказано в доступе
12:08:21
$vim install
12:12:29
$cat ../install
#!/bin/sh
hostname=`hostname`
uname -a | grep -qi freebsd || hostname=`hostname -f`
###############################################################################
#
# Set this variables before installation:
lilalo_user=${lilalo_user:-su}
lab=${lab:-mrg}
install_l3bashrc_for_this_users=${users:-"root su kos_aa"}  # users who will use l3agent and l3script
lilalo_context="/users/${lilalo_user}/${lab}/${hostname}"
...
step "Downloading l3prompt" ${wget} ${url_l3prompt}
step "Downloading l3-agent" '${wget} ${url_l3agent}; ${wget} ${url_l3config_pm}; ${wget} ${url_l3config}'
step "Downloading perl modules for l3-agent" '{ for i in ${perl_modules}; do ${wget} ${url_perl_modules}/$i.tar.gz; done; }'
step "Installing perl modules for l3-agent" '{ for i in ${perl_modules}; do tar xvfz $i.tar.gz; cd $i*[^z]; perl Makefile.PL; make; make install; cd ..; done; }'
step "Installing l3bashrc to users home directories" install_to_users_homes $install_l3bashrc_for_this_users
step "Adding l3bashrc invocation to ~/.bashrc " install_to_users_bashrc $install_l3bashrc_for_this_users
step "Adding l3-agent invocation to ~/.bash_profile " install_to_users_bash_profile $install_l3bashrc_for_this_users
cd /
rm -rf ${temp_dir}
show_final_message
12:12:33
$cat /etc/li
libao.conf           lilalo/              lilo.conf.old        linbilling.cfg
libaudit.conf        lilo.conf            lilo.conf.save       linbilling_rule.cfg
12:12:33
$cat /etc/lilalo/
cat: /etc/lilalo/: Это каталог
12:12:56
$cat /etc/lilalo/l3config.pm
package l3config;
use utf8;
use Exporter;
use vars qw(@ISA @EXPORT $VERSION);
use Getopt::Long;
@ISA = ('Exporter');
@EXPORT = qw(%Config &init_config);
our $System_Config_File = "/etc/lilalo.conf";
our $User_Config_File = "$ENV{HOME}/.l3rc";
$ENV{HOME} ||= "/tmp";
...
        my %argv_config;
        my %file_config;
        read_config_file(\%file_config, $System_Config_File);
        read_config_file(\%file_config, $User_Config_File);
        GetOptions(\%argv_config, map "$_=s", keys %Config);
        %Config = (%Config, %file_config, %argv_config);
    for my $key (keys %Config) {
        utf8::decode($Config{$key});
    }
}
12:12:58
$l
итого 428
drwxrwxrwt 13 root root   640 Сен  9 14:14 ../
drwxr-xr-x  8 su   su     860 Сен  9 14:12 ./
-rwxr-xr-x  1 su   su   32757 Мар 16  2008 l3-agent
drwxr-xr-x  2 su   su     100 Мар 13  2008 CVS/
-rwxr-xr-x  1 su   su    7522 Мар 13  2008 l3bashrc
-rwxr-xr-x  1 su   su   78084 Мар 13  2008 l3-frontend
-rw-r--r--  1 su   su    5781 Мар 10  2008 l3config.pm
-rwxr-xr-x  1 su   su    5168 Мар 10  2008 install
-rwxr-xr-x  1 su   su     234 Мар 10  2008 l3prompt
...
-rw-r--r--  1 su   su    1598 Ноя  3  2005 tail-all.pl
-rwxr-xr-x  1 su   su      47 Ноя  2  2005 l3-report
-rwxr-xr-x  1 su   su   31413 Ноя  2  2005 lm-report
-rw-r--r--  1 su   su    4162 Окт 22  2005 lilalo.kdi
drwxr-xr-x  3 su   su     160 Май 22  2005 share/
-rw-r--r--  1 su   su    1496 Май 22  2005 FILES
-rw-r--r--  1 su   su     452 Май 22  2005 INSTALL
-rwxr-xr-x  1 su   su     316 Май 22  2005 lm-ssh
-rw-r--r--  1 su   su     157 Май 22  2005 taillast.pl
-rw-r--r--  1 su   su     111 Май 22  2005 .tarball
12:14:18
$cat l3
l3-agent     l3-cgi       l3config.pm  l3prompt     l3-upload
l3-backend   l3-cgi-lite  l3files/     l3-report
l3bashrc     l3-config    l3-frontend  l3scripts
12:14:18
$cat l3-frontend
#!/usr/bin/perl -w
use POSIX qw(strftime);
use lib '/etc/lilalo';
use l3config;
use utf8;
our @Command_Lines;
our @Command_Lines_Index;
our %Commands_Description;
our %Args_Description;
our %Sessions;
...
            $result .= "($section)" if $section;
        }
        else {
            1 while ($result =~ s/(\s+)-(\s+)/$1+$2/sg);
            $result =~ s/\s+\(/(/;
            chomp $result;
        }
    }
    return $result;
}
12:14:21
$./l3
l3-agent     l3bashrc     l3-cgi-lite  l3files/     l3prompt     l3-upload
l3-backend   l3-cgi       l3-config    l3-frontend  l3-report
12:14:21
$./l3-frontend
Can't open /home/igor/mywi/mywi.txt for reading at ./l3-frontend line 2046.
12:14:57
$vim l3-frontend
12:15:23
$cat l3
l3-agent     l3-cgi       l3config.pm  l3prompt     l3-upload
l3-backend   l3-cgi-lite  l3files/     l3-report
l3bashrc     l3-config    l3-frontend  l3scripts
12:15:23
$cat l3-config
#!/usr/bin/perl
use strict;
use lib '/etc/lilalo/';
use l3config;
print $Config{$ARGV[0]}."\n";
12:15:30
$vim l3-frontend
12:18:17
$cd /home/su/.lilalo/

12:18:24
$l
итого 402
-rw-r--r--  1 su   su   148385 Сен  9 14:18 268158378436214913-1315560387.script
-rw-r--r--  1 su   su     2413 Сен  9 14:18 268158378436214913-1315560387.info
-rw-r--r--  1 su   su      202 Сен  9 14:18 .report.dat
-rw-r--r--  1 su   su   153725 Сен  9 14:18 report.xml
drwxr-xr-x  2 su   root    864 Сен  9 14:18 ./
drwx------ 30 su   su     1464 Сен  9 14:18 ../
-rw-r--r--  1 su   su        0 Сен  9 14:18 268158378436214913-1315560387_1315563340_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su        0 Сен  9 14:15 268158378436214913-1315560387_1315563309_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su        0 Сен  9 14:12 268158378436214913-1315560387_1315563126_tmp_lilalo_install.diff
-rw-r--r--  1 su   su        5 Сен  9 13:31 l3-agent.pid
-rw-r--r--  1 root root  21573 Сен  9 13:26 2304020058671131785-1315560283.info
-rw-r--r--  1 root root  10326 Сен  9 13:26 2304020058671131785-1315560283.script
-rw-r--r--  1 su   su    24352 Сен  9 13:26 3264330288285888354-1315560130.info
-rw-r--r--  1 su   su    11507 Сен  9 13:26 3264330288285888354-1315560130.script
-rw-r--r--  1 root root    315 Сен  9 13:25 2304020058671131785-1315560283_1315560315_root_.bashrc.diff
-rwxr-xr-x  1 su   root   7709 Сен  9 13:15 l3bashrc
-rwxr-xr-x  1 su   root    234 Сен  9 13:15 l3prompt
12:18:25
$cat l3-agent.pid

12:18:39
$cat l3-agent.pid

12:18:42
$cat 268158378436214913-1315560387.scrip
cat: 268158378436214913-1315560387.scrip: Нет такого файла или каталога
12:18:53
$cat 268158378436214913-1315560387.script
Скрипт запущен Птн 09 Сен 2011 13:26:27
12:19:11
$1;2801;0c1;2801;0c
bash: 1: команда не найдена
bash: 2801: команда не найдена
bash: 0c1: команда не найдена
bash: 2801: команда не найдена
bash: 0c: команда не найдена
12:19:20
$l3-
l3-agent    l3-backend  l3-config
12:19:20
$l3-agent --help
Unknown option: help
l3-agent is already running: pid=12491; pidfile=/home/su/.lilalo/l3-agent.pid
12:19:42
$l3-config

12:19:46
$l3-config --hrlp

12:19:49
$l3-config --help

12:19:52
$ps fax
  PID TTY      STAT   TIME COMMAND
    2 ?        S<     0:00 [kthreadd]
    3 ?        S<     0:00  \_ [migration/0]
    4 ?        S<     0:01  \_ [ksoftirqd/0]
    5 ?        S<     0:00  \_ [watchdog/0]
    6 ?        S<     0:00  \_ [migration/1]
    7 ?        S<     0:05  \_ [ksoftirqd/1]
    8 ?        S<     0:00  \_ [watchdog/1]
    9 ?        S<     0:00  \_ [migration/2]
   10 ?        S<     0:05  \_ [ksoftirqd/2]
...
28267 ?        S      0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
28268 ?        S      0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
28269 ?        S      0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
28270 ?        S      0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
28271 ?        S      0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
28272 ?        S      0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
28273 ?        S      0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
22409 ?        SNl    1:37 /opt/openfire/jre/bin/java -server -DopenfireHome=/opt/openf
18607 ?        SNs    7:49 /sbin/syslogd -u syslogd -j /var/resolv
12491 ?        Ss     5:23 l3-agent
прошло 11 минут
12:31:05
$ps fax | grep l3
22747 pts/1    S+     0:00                      \_ grep l3
12491 ?        Ss     5:24 l3-agent
12:31:10
$whic hl3-agent
bash: whic: команда не найдена
12:31:18
$which l3-agent
/usr/local/bin/l3-agent
12:31:21
$cat /usr/local/bin/l3-agent
#!/usr/bin/perl -w
#
# (c) Igor Chubin, igor@chub.in, 2004-2008
#
use strict;
use POSIX;
use Term::VT102;
use Text::Iconv;
use Time::Local 'timelocal_nocheck';
use IO::Socket;
...
                send_cache() && unlink($Config{cache});
            }
            sleep($Config{"daemon_sleep_interval"} || 1);
        }
        unlink $Config{agent_pidfile};
    }
}
sub init_variables
{
}
12:32:19
$sudo grep 129 /config/firewall/useracl
#iptables -A useracl -s 10.10.120.129 -d nnm-club.ru -j ULOG --ulog-prefix "Kosarev NNM  "
#iptables -A useracl -s 10.10.120.129 -d nnm-club.ru -j ACCEPT #mail aent Kosarev
iptables -A useracl -s 10.10.120.129 -d nnm-club.ru -j ULOG --ulog-prefix "Kosarev NNM  "
iptables -A useracl -s 10.10.120.129 -d nnm-club.ru -j ACCEPT #Kosarev NNM
iptables -A useracl -p tcp -s 10.10.120.129 -d u69073.ftp.masterhost.ru -j ULOG --ulog-prefix "Masterhost   "
iptables -A useracl -p tcp -s 10.10.120.129 -d u69073.ftp.masterhost.ru -j ACCEPT #Masterhost
iptables -A useracl -p tcp -s 10.10.120.129 -d nichost.ru -j ULOG --ulog-prefix "NIC FTP  "
iptables -A useracl -p tcp -s 10.10.120.129 -d nichost.ru -j ACCEPT #NIC FTP
iptables -A useracl -p tcp -s 10.10.120.129 -d s14.h.mchost.ru -j ULOG --ulog-prefix "MCHOST FTP  "
iptables -A useracl -p tcp -s 10.10.120.129 -d s14.h.mchost.ru -j ACCEPT #MCHOST FTP
iptables -A useracl -p tcp -s 10.10.120.129 -d 0/0 --dport 20:21 -j ULOG --ulog-prefix "Kosarev FTP   "
iptables -A useracl -p tcp -s 10.10.120.129 -d 0/0 --dport 20:21 -j ACCEPT #Kosarev FTP
iptables -A useracl -p tcp -s 10.10.120.129 -d 0/0 --dport 2041:2042 -j ULOG --ulog-prefix "Kosarev MRIM   "
iptables -A useracl -p tcp -s 10.10.120.129 -d 0/0 --dport 2041:2042 -j ACCEPT #Kosarev MRIM
iptables -A useracl -p tcp -s 10.10.120.129 -d 90.156.201.0/24 --dport 80 -j ULOG --ulog-prefix "Kosarev WWW  "
iptables -A useracl -p tcp -s 10.10.120.129 -d 90.156.201.17 --dport 80 -j ACCEPT #Kosarev WWW
iptables -A useracl -p tcp -s 10.10.120.129 -d 90.156.201.23 --dport 80 -j ACCEPT #Kosarev WWW
iptables -A useracl -p tcp -s 10.10.120.129 -d 90.156.201.40 --dport 80 -j ACCEPT #Kosarev WWW
iptables -A useracl -p tcp -s 10.10.120.129 -d 90.156.201.41 --dport 80 -j ACCEPT #Kosarev WWW
12:32:30
$iptables -L -v | grep 120.129
bash: iptables: команда не найдена
12:33:02
$sudo iptables -L -v -n | grep 120.129
    0     0 ULOG       all  --  *      *       10.10.120.129        193.107.211.4       ULOG copy_range 0 nlgroup 1 prefix `Kosarev NNM  ' queue_threshold 1
    0     0 ACCEPT     all  --  *      *       10.10.120.129        193.107.211.4
  309  195K ULOG       tcp  --  *      *       10.10.120.129        90.156.199.78       ULOG copy_range 0 nlgroup 1 prefix `Masterhost   ' queue_threshold 1
  309  195K ACCEPT     tcp  --  *      *       10.10.120.129        90.156.199.78
    0     0 ULOG       tcp  --  *      *       10.10.120.129        194.85.61.54        ULOG copy_range 0 nlgroup 1 prefix `NIC FTP  ' queue_threshold 1
    0     0 ACCEPT     tcp  --  *      *       10.10.120.129        194.85.61.54
    0     0 ULOG       tcp  --  *      *       10.10.120.129        178.208.83.18       ULOG copy_range 0 nlgroup 1 prefix `MCHOST FTP  ' queue_threshold 1
    0     0 ACCEPT     tcp  --  *      *       10.10.120.129        178.208.83.18
   37  1981 ULOG       tcp  --  *      *       10.10.120.129        0.0.0.0/0           tcp dpts:20:21 ULOG copy_range 0 nlgroup 1 prefix `Kosarev FTP   ' queue_threshold 1
   37  1981 ACCEPT     tcp  --  *      *       10.10.120.129        0.0.0.0/0           tcp dpts:20:21
  997 69004 ULOG       tcp  --  *      *       10.10.120.129        0.0.0.0/0           tcp dpts:2041:2042 ULOG copy_range 0 nlgroup 1 prefix `Kosarev MRIM   ' queue_threshold 1
  997 69004 ACCEPT     tcp  --  *      *       10.10.120.129        0.0.0.0/0           tcp dpts:2041:2042
    0     0 ULOG       tcp  --  *      *       10.10.120.129        90.156.201.0/24     tcp dpt:80 ULOG copy_range 0 nlgroup 1 prefix `Kosarev WWW  ' queue_threshold 1
    0     0 ACCEPT     tcp  --  *      *       10.10.120.129        90.156.201.17       tcp dpt:80
    0     0 ACCEPT     tcp  --  *      *       10.10.120.129        90.156.201.23       tcp dpt:80
    0     0 ACCEPT     tcp  --  *      *       10.10.120.129        90.156.201.40       tcp dpt:80
    0     0 ACCEPT     tcp  --  *      *       10.10.120.129        90.156.201.41       tcp dpt:80
12:33:05
$sudo su

Понедельник (09/12/11)

/dev/pts/1
05:56:27
#l
total 8029
drwxr-xr-x  2 su   root     976 Sep 12 07:56 ./
-rw-r--r--  1 su   su       204 Sep 12 07:56 .report.dat
-rw-r--r--  1 root root     456 Sep 12 07:56 10350288104344998-1315799787.info
-rw-r--r--  1 root root     253 Sep 12 07:56 10350288104344998-1315799787.script
-rw-r--r--  1 su   su   7858786 Sep 12 07:56 268158378436214913-1315560387.script
-rw-r--r--  1 su   su    237601 Sep  9 14:33 report.xml
-rw-r--r--  1 su   su      3859 Sep  9 14:33 268158378436214913-1315560387.info
drwx------ 30 su   su      1464 Sep  9 14:18 ../
-rw-r--r--  1 su   su         0 Sep  9 14:18 268158378436214913-1315560387_1315563340_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su         0 Sep  9 14:15 268158378436214913-1315560387_1315563309_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su         0 Sep  9 14:12 268158378436214913-1315560387_1315563126_tmp_lilalo_install.diff
-rw-r--r--  1 su   su         5 Sep  9 13:31 l3-agent.pid
-rw-r--r--  1 root root   21573 Sep  9 13:26 2304020058671131785-1315560283.info
-rw-r--r--  1 root root   10326 Sep  9 13:26 2304020058671131785-1315560283.script
-rw-r--r--  1 su   su     24352 Sep  9 13:26 3264330288285888354-1315560130.info
-rw-r--r--  1 su   su     11507 Sep  9 13:26 3264330288285888354-1315560130.script
-rw-r--r--  1 root root     315 Sep  9 13:25 2304020058671131785-1315560283_1315560315_root_.bashrc.diff
-rwxr-xr-x  1 su   root    7709 Sep  9 13:15 l3bashrc
-rwxr-xr-x  1 su   root     234 Sep  9 13:15 l3prompt
05:56:27
#cd /home/kos_aa/.l
.l3rc       .lftp/      .lilalo/    .lpoptions
05:56:27
#cd /home/kos_aa/.lilalo/

/dev/pts/0
05:56:27
#l
total 8029
drwxr-xr-x  2 su   root     976 Sep 12 07:56 ./
-rw-r--r--  1 su   su       204 Sep 12 07:56 .report.dat
-rw-r--r--  1 root root     456 Sep 12 07:56 10350288104344998-1315799787.info
-rw-r--r--  1 root root     253 Sep 12 07:56 10350288104344998-1315799787.script
-rw-r--r--  1 su   su   7858786 Sep 12 07:56 268158378436214913-1315560387.script
-rw-r--r--  1 su   su    237601 Sep  9 14:33 report.xml
-rw-r--r--  1 su   su      3859 Sep  9 14:33 268158378436214913-1315560387.info
drwx------ 30 su   su      1464 Sep  9 14:18 ../
-rw-r--r--  1 su   su         0 Sep  9 14:18 268158378436214913-1315560387_1315563340_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su         0 Sep  9 14:15 268158378436214913-1315560387_1315563309_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su         0 Sep  9 14:12 268158378436214913-1315560387_1315563126_tmp_lilalo_install.diff
-rw-r--r--  1 su   su         5 Sep  9 13:31 l3-agent.pid
-rw-r--r--  1 root root   21573 Sep  9 13:26 2304020058671131785-1315560283.info
-rw-r--r--  1 root root   10326 Sep  9 13:26 2304020058671131785-1315560283.script
-rw-r--r--  1 su   su     24352 Sep  9 13:26 3264330288285888354-1315560130.info
-rw-r--r--  1 su   su     11507 Sep  9 13:26 3264330288285888354-1315560130.script
-rw-r--r--  1 root root     315 Sep  9 13:25 2304020058671131785-1315560283_1315560315_root_.bashrc.diff
-rwxr-xr-x  1 su   root    7709 Sep  9 13:15 l3bashrc
-rwxr-xr-x  1 su   root     234 Sep  9 13:15 l3prompt
05:56:27
#cd /home/kos_aa/.l
.l3rc       .lftp/      .lilalo/    .lpoptions
05:56:27
#cd /home/kos_aa/.lilalo/

/dev/pts/1
05:56:35
#l
total 13
drwx------ 12 kos_aa kos_aa  568 Sep  9 13:27 ../
-rw-r--r--  1 kos_aa root   7709 Sep  9 13:15 l3bashrc
-rwxr-xr-x  1 kos_aa root    234 Sep  9 13:15 l3prompt
drwxr-xr-x  2 kos_aa root     96 Sep  9 11:00 ./
05:56:35
#cd

/dev/pts/0
05:56:35
#l
total 13
drwx------ 12 kos_aa kos_aa  568 Sep  9 13:27 ../
-rw-r--r--  1 kos_aa root   7709 Sep  9 13:15 l3bashrc
-rwxr-xr-x  1 kos_aa root    234 Sep  9 13:15 l3prompt
drwxr-xr-x  2 kos_aa root     96 Sep  9 11:00 ./
05:56:35
#cd

05:56:37
#exit

05:56:38
$sudo su

прошло 97 минут
/dev/pts/1
07:33:59
#l
total 8134
drwxr-xr-x  2 su   root    1088 Sep 12 09:33 ./
-rw-r--r--  1 root root     444 Sep 12 09:33 2566179733199511555-1315805639.info
-rw-r--r--  1 root root     253 Sep 12 09:33 2566179733199511555-1315805639.script
-rw-r--r--  1 su   su   7862170 Sep 12 09:33 268158378436214913-1315560387.script
-rw-r--r--  1 su   su       267 Sep 12 09:33 .report.dat
-rw-r--r--  1 root root   21360 Sep 12 07:56 10350288104344998-1315799787.info
-rw-r--r--  1 root root    3212 Sep 12 07:56 10350288104344998-1315799787.script
-rw-r--r--  1 su   su      3912 Sep 12 07:56 268158378436214913-1315560387.info
-rw-r--r--  1 su   su    248231 Sep 12 07:56 report.xml
...
-rw-r--r--  1 su   su         0 Sep  9 14:15 268158378436214913-1315560387_1315563309_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su         0 Sep  9 14:12 268158378436214913-1315560387_1315563126_tmp_lilalo_install.diff
-rw-r--r--  1 su   su         5 Sep  9 13:31 l3-agent.pid
-rw-r--r--  1 root root   21573 Sep  9 13:26 2304020058671131785-1315560283.info
-rw-r--r--  1 root root   10326 Sep  9 13:26 2304020058671131785-1315560283.script
-rw-r--r--  1 su   su     24352 Sep  9 13:26 3264330288285888354-1315560130.info
-rw-r--r--  1 su   su     11507 Sep  9 13:26 3264330288285888354-1315560130.script
-rw-r--r--  1 root root     315 Sep  9 13:25 2304020058671131785-1315560283_1315560315_root_.bashrc.diff
-rwxr-xr-x  1 su   root    7709 Sep  9 13:15 l3bashrc
-rwxr-xr-x  1 su   root     234 Sep  9 13:15 l3prompt
07:33:59
#cd /usr/local/sbin/

/dev/pts/0
07:33:59
#l
total 8134
drwxr-xr-x  2 su   root    1088 Sep 12 09:33 ./
-rw-r--r--  1 root root     444 Sep 12 09:33 2566179733199511555-1315805639.info
-rw-r--r--  1 root root     253 Sep 12 09:33 2566179733199511555-1315805639.script
-rw-r--r--  1 su   su   7862170 Sep 12 09:33 268158378436214913-1315560387.script
-rw-r--r--  1 su   su       267 Sep 12 09:33 .report.dat
-rw-r--r--  1 root root   21360 Sep 12 07:56 10350288104344998-1315799787.info
-rw-r--r--  1 root root    3212 Sep 12 07:56 10350288104344998-1315799787.script
-rw-r--r--  1 su   su      3912 Sep 12 07:56 268158378436214913-1315560387.info
-rw-r--r--  1 su   su    248231 Sep 12 07:56 report.xml
...
-rw-r--r--  1 su   su         0 Sep  9 14:15 268158378436214913-1315560387_1315563309_tmp_lilalo_l3-frontend.diff
-rw-r--r--  1 su   su         0 Sep  9 14:12 268158378436214913-1315560387_1315563126_tmp_lilalo_install.diff
-rw-r--r--  1 su   su         5 Sep  9 13:31 l3-agent.pid
-rw-r--r--  1 root root   21573 Sep  9 13:26 2304020058671131785-1315560283.info
-rw-r--r--  1 root root   10326 Sep  9 13:26 2304020058671131785-1315560283.script
-rw-r--r--  1 su   su     24352 Sep  9 13:26 3264330288285888354-1315560130.info
-rw-r--r--  1 su   su     11507 Sep  9 13:26 3264330288285888354-1315560130.script
-rw-r--r--  1 root root     315 Sep  9 13:25 2304020058671131785-1315560283_1315560315_root_.bashrc.diff
-rwxr-xr-x  1 su   root    7709 Sep  9 13:15 l3bashrc
-rwxr-xr-x  1 su   root     234 Sep  9 13:15 l3prompt
07:33:59
#cd /usr/local/sbin/

/dev/pts/1
07:34:01
#l
total 703
drwxr-xr-x 16 root root    384 Sep  7 16:51 ../
drwxr-xr-x  4 su   root   2696 Jul 18 16:07 ./
-rwxr-xr-x  1 root root   4701 Jul 18 16:07 AutoCreateWhite_List
-rwxr-xr-x  1 root root    268 Jun  8 09:08 Check_Squiddaemon
-rwxr-xr-x  1 root root    265 Jun  8 09:05 Check_Samsdaemon
-rwx------  1 root root    672 Jun  8 09:04 Spamheaders
-rwxr-xr-x  1 su   root   2248 Jun  8 09:04 ShutdownUrodov
-rwxr-xr-x  1 root root   1840 Jun  8 09:04 NUlog
-rwx------  1 root root   1240 Jun  8 09:03 MaxSize
...
-rwx------  1 root root     71 Mar  4  2010 CGP_logs
-rwx------  1 root root    760 Mar  4  2010 Fired_Users
-rwx------  1 root root    795 Mar  4  2010 MailFromSiteMrgarant.ru
-rw-------  1 root root    748 Mar  4  2010 ParseExcel
-rwx------  1 root root    159 Mar  4  2010 Sa-learn
-rwxr-xr-x  1 root root   6188 Mar  4  2010 faxanswer
-rwxr-xr-x  1 root root   6300 Mar  4  2010 faxconfig
-rwxr-xr-x  1 root root   6144 Mar  4  2010 faxdeluser
-rwx------  1 root root  46247 Mar  6  2004 xcode
-rw-rw-r--  1 root root    222 Mar  6  2004 xcodeln.pl
/dev/pts/0
07:34:01
#l
total 703
drwxr-xr-x 16 root root    384 Sep  7 16:51 ../
drwxr-xr-x  4 su   root   2696 Jul 18 16:07 ./
-rwxr-xr-x  1 root root   4701 Jul 18 16:07 AutoCreateWhite_List
-rwxr-xr-x  1 root root    268 Jun  8 09:08 Check_Squiddaemon
-rwxr-xr-x  1 root root    265 Jun  8 09:05 Check_Samsdaemon
-rwx------  1 root root    672 Jun  8 09:04 Spamheaders
-rwxr-xr-x  1 su   root   2248 Jun  8 09:04 ShutdownUrodov
-rwxr-xr-x  1 root root   1840 Jun  8 09:04 NUlog
-rwx------  1 root root   1240 Jun  8 09:03 MaxSize
...
-rwx------  1 root root     71 Mar  4  2010 CGP_logs
-rwx------  1 root root    760 Mar  4  2010 Fired_Users
-rwx------  1 root root    795 Mar  4  2010 MailFromSiteMrgarant.ru
-rw-------  1 root root    748 Mar  4  2010 ParseExcel
-rwx------  1 root root    159 Mar  4  2010 Sa-learn
-rwxr-xr-x  1 root root   6188 Mar  4  2010 faxanswer
-rwxr-xr-x  1 root root   6300 Mar  4  2010 faxconfig
-rwxr-xr-x  1 root root   6144 Mar  4  2010 faxdeluser
-rwx------  1 root root  46247 Mar  6  2004 xcode
-rw-rw-r--  1 root root    222 Mar  6  2004 xcodeln.pl

Файлы

  • ../install
  • .bashrc
  • /etc/li
  • /etc/lilalo/l3config.pm
  • /home/su/.bashrc
  • /usr/local/bin/l3-agent
  • 268158378436214913-1315560387.script
  • l3
  • l3-agent.pid
  • l3-backend
  • l3-config
  • l3-frontend
  • ../install
    >
    #!/bin/sh
    hostname=`hostname`
    uname -a | grep -qi freebsd || hostname=`hostname -f`
    ###############################################################################
    #
    # Set this variables before installation:
    lilalo_user=${lilalo_user:-su}
    lab=${lab:-mrg}
    install_l3bashrc_for_this_users=${users:-"root su kos_aa"}  # users who will use l3agent and l3script
    lilalo_context="/users/${lilalo_user}/${lab}/${hostname}"
    #
    ###############################################################################
    lilalo_rc=.l3rc
    lilalo_home=.lilalo
    url_lilalo="http://xgu.ru/lilalo"
    url_l3bashrc="${url_lilalo}"/l3bashrc
    url_l3agent="${url_lilalo}"/l3-agent
    url_l3config_pm="${url_lilalo}"/l3config.pm
    url_l3config="${url_lilalo}"/l3-config
    url_l3prompt="${url_lilalo}"/l3prompt
    url_perl_modules=${url_lilalo}/
    perl_modules="Term-VT102 Text-Iconv"
    apt_get_install_this="perl make libmodule-build-perl libc6-dev gcc"
    wget=wget
    uname -a | grep -qi bsd && wget=fetch
    normC='\033[0;39m'
    whiteC='\033[1;37m'
    redC='\033[0;31m'
    greenC='\033[0;32m'
    apt_get_install_deps()
    {
        return 0
        if which apt-get >& /dev/null
        then
            apt-get install -y $apt_get_install_this
        else
            echo "Please install this dependencies manually:"
            echo $apt_get_install_this
            echo "Have you installed this already (y/n)?"
            echo y | read answer
            if echo $answer | grep -q ^[yY]
            then
                true
            else
                echo Please install the dependencies and rerun the script
                exit 1
            fi
        fi
    }
    step()
    {
        msg="$1"
        shift
        printf "${whiteC}""$msg""...${normC}\n"
    #    eval "$@" 2>&1 | sed 's/^/|\ \ \ /' && printf "Ok\n" || printf "Failed\n"
        eval "$@" 2>&1 > log 2>&1 && \
        {
              cat log | sed 's/^/|\ \ \ /'
              printf "${greenC}""Ok\n""${normC}"
        } || \
        {
            cat log | sed 's/^/|\ \ \ /'
            printf "${redC}""Failed\n""${normC}"
        }
    }
    get_user_home()
    {
        uname -a | grep -qi freebsd && pw user show "$@"| awk -F: '{print $9}' || getent passwd "$@"| awk -F: '{print $6}'
    }
    install_to_users_homes()
    {
        . l3bashrc
        users="$@"
        set -x
        for user in $users
        do
            user_home=`get_user_home "$user"`
            mkdir -p ${user_home}/${lilalo_home}
            mkdir /etc/lilalo/
            cp l3config.pm /etc/lilalo/
            cp l3-agent /usr/local/bin
            cp l3-config /usr/local/bin
            ln -s `which bash` /usr/local/bin/l3script
            chmod 755 /usr/local/bin/l3-{agent,config}
            cp l3bashrc ${user_home}/${lilalo_home}
            cp l3prompt ${user_home}/${lilalo_home}
            chmod 755 ${user_home}/${lilalo_home}/l3prompt
            chown -R $user ${user_home}/${lilalo_home}
            echo l3cd=${lilalo_context}/$user > ${user_home}/${lilalo_rc}
            chown -R $user ${user_home}/${lilalo_rc}
        done
        set +x
    }
    install_to_users_bashrc()
    {
        users="$@"
        for user in $users
        do
            user_home=`get_user_home "$user"`
            grep -q lilalo ${user_home}/.bashrc 2> /dev/null\
            || echo "[ \$0 == l3script ] && . ${user_home}/.lilalo/l3bashrc && _l3_start" >> ${user_home}/.bashrc; chown -R ${user} ${user_home}/.bashrc
        done
    }
    install_to_users_bash_profile()
    {
        users="$@"
        for user in $users
        do
            user_home=`get_user_home "$user"`
            grep -q l3-agent ${user_home}/.bash_profile 2> /dev/null \
            || { echo >> ${user_home}/.bash_profile ; cat ${user_home}/.bash_profile | sed '1s/^/l3-agentX/' | tr X '\n' > /tmp/$$$$l3 ; mv /tmp/$$$$l3 ${user_home}/.bash_profile; chown -R ${user} ${user_home}/.bash_profile; }
        done
    }
    show_usage()
    {
        cat <<USAGE
    Usage:
        $0
    USAGE
    }
    show_final_message()
    {
        cat <<FINAL_MESSAGE
    Installation is successfully completed.
    Now restart your shell or relogin
    to start script writing.
    Your current lilalo context is ${lilalo_context}/USER
    If you use xgu.ru backend, your labs will be available at
    http://xgu.ru/l3/${lilalo_context}
    Use commands
     $ l3cd ${lilalo_context%/*/*}/MY-NEW-CONTEXT/${hostname}/USER
     $ l3pwd
    to change and to know your current context.
    For further information see http://xgu.ru/lilalo/ (in Russian).
    Thank you gor using LiLaLo.
    Happy Labbing!
    (don't forget to restart bash or relogin)
    FINAL_MESSAGE
    }
    temp_dir=/tmp/lilalo-install-temp-$$
    mkdir -p ${temp_dir}
    cd ${temp_dir}
    step "Installing dependencies" apt_get_install_deps
    step "Downloading l3bashrc" ${wget} ${url_l3bashrc}
    step "Downloading l3prompt" ${wget} ${url_l3prompt}
    step "Downloading l3-agent" '${wget} ${url_l3agent}; ${wget} ${url_l3config_pm}; ${wget} ${url_l3config}'
    step "Downloading perl modules for l3-agent" '{ for i in ${perl_modules}; do ${wget} ${url_perl_modules}/$i.tar.gz; done; }'
    step "Installing perl modules for l3-agent" '{ for i in ${perl_modules}; do tar xvfz $i.tar.gz; cd $i*[^z]; perl Makefile.PL; make; make install; cd ..; done; }'
    step "Installing l3bashrc to users home directories" install_to_users_homes $install_l3bashrc_for_this_users
    step "Adding l3bashrc invocation to ~/.bashrc " install_to_users_bashrc $install_l3bashrc_for_this_users
    step "Adding l3-agent invocation to ~/.bash_profile " install_to_users_bash_profile $install_l3bashrc_for_this_users
    cd /
    rm -rf ${temp_dir}
    show_final_message
    
    .bashrc
    >
    # .bashrc
    # User specific aliases and functions
    alias cp='cp -i'
    alias mv='mv -i'
    alias rm='rm -i'
    alias d='ls'
    alias s='cd ..'
    alias p='cd -'
    # Read /etc/inputrc if the variable is not defined.
    [ -n "$INPUTRC" ] || export INPUTRC=/etc/inputrc
    # Source global definitions
    if [ -f /etc/bashrc ]; then
            . /etc/bashrc
    fi
    PATH=/root/bin:/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin
    ENV=$HOME/.bashrc
    USERNAME="root"
    export USERNAME ENV PATH
    #[ $0 == l3script ] && . /root/.lilalo/l3bashrc && _l3_start
    . ${user_home}/.lilalo/l3bashrc && _l3_start
    
    /etc/li
    >
    libao.conf           lilalo/              lilo.conf.old        linbilling.cfg
    libaudit.conf        lilo.conf            lilo.conf.save       linbilling_rule.cfg
    
    /etc/lilalo/l3config.pm
    >
    package l3config;
    use utf8;
    use Exporter;
    use vars qw(@ISA @EXPORT $VERSION);
    use Getopt::Long;
    @ISA = ('Exporter');
    @EXPORT = qw(%Config &init_config);
    our $System_Config_File = "/etc/lilalo.conf";
    our $User_Config_File = "$ENV{HOME}/.l3rc";
    $ENV{HOME} ||= "/tmp";
    our %Config = (
        "skip_empty"        => "yes",
        "skip_interrupted"  => "no",
        "skip_wrong"        => "no",
        "editors"           => ["vi", "pico", "ee", "vim", "nano"],
        "pagers"            => ["more", "less", "zmore", "zless", "info",
                                "man", "mc", "trafshow", "screen", "cfdisk",
                                "trafshow-bsd", "yes", "lynx", "links", "centericq"
                                ],
        "full_output_commands" => ["cat"],
        "terminal"          => ["mc"],
        "suppress_editors"  => "yes",
        "suppress_pagers"   => "yes",
        "suppress_terminal" => "yes",
        "terminal_width"    => 400,
        "terminal_height"   => 1000,
        "verbose"           => "yes",
        "head_lines"        => 10,
        "tail_lines"        => 10,
        "cache_head_lines"  => 2500,
        "cache_tail_lines"  => 2500,
        "skip_text"         => "...",
        "show_time"         => "yes",
        "show_diffs"        => "yes",
        "show_screenshots"  => "yes",
        "show_comments"     => "yes",
        "show_notes"        => "yes",
        "input"             => "$ENV{HOME}/.lilalo",
        "diffs"             => "",
        "input_mask"        => "*.script",
        "encoding"          => "utf-8",
        "cache"             => "$ENV{HOME}/.lilalo/report.xml",
        "cache_stat"        => "$ENV{HOME}/.lilalo/.report.dat",
        "output"            => "/tmp/report.html",
        "output_mask"       => "INDEX",
        "output_format"     => "html",
        "cgi_path"          => "/l3",
        "frontend_files"    => "/l3files",
        "frontend_css"      => "/l3files/l3.css",
        "l3shot_path"       => "/l3shot/",
        "l3shot_suffix"       => ".png",
        "frontend_google_ico"   => "/l3/google.ico",
        "frontend_linux_ico"    => "/l3/linux.ico",
        "frontend_freebsd_ico"  => "/l3/freebsd.ico",
        "frontend_opennet_ico"  => "/l3/opennet.ico",
        "frontend_local_ico"    => "/l3/freebsd.ico",
        "mywi_server"       => "127.0.0.1",
        "mywi_port"         => "19801",
        "stat_inactivity_interval"  =>  "1800",
        "signature"         => "#lm:",
        "from"              => "",
        "to"                => "",
        "lab"               => "",
        "keywords"          => "linux command",
        "files_keywords"    => "linux file",
        comment_width       => "300",
        note_width          => "500",
        time_width          => "6em",
        "mode"              => "daemon",        # daemon | normal
        "daemon_sleep_interval" => "10",
        "detach"            => "yes",
        "agent_pidfile"     => "$ENV{HOME}/.lilalo/l3-agent.pid",
        "backend_address"   => "xgu.ru",
        "backend_port"      => "18030",
        "backend_pidfile"   => "/tmp/l3-backend.pid",
        "backend_datafile"  => "/var/lilalo/lablogs-xml/backend.xml",
        "backend_datadir"  => "/var/lilalo/lablogs-xml/",
        "upload_dir"        => "/var/www/l3shot",
        "l3-agent"          => "l3-agent",
        "l3-backend"        => "l3-backend",
        "course-name"       => "",
        "course-code"       => "",
        "course-date"       => "",
        "course-center"     => "",
        "course-trainer"    => "",
        "course-student"    => "",
        "filter"            => "",
    #lm
        "show_host"         => "no",
        "l3cd"              => "",          # Текущий контекст перехваченных команд
                                            # Возможные варианты:
                                            #   КУРС/ДАТА-НАЧАÐ
                   #   ДАТА-НАЧАÐ
                            #   УНИКАЗОВАТЕÐ
    3-report"    => "./lm-report",
        "l3-report"         => "./l3-report",
    # Каталоги
        "path_lilalo"       => "/var/lilalo/",
        "path_classes"      => "/var/lilalo/classes/",
        "path_lablogs"      => "/var/lilalo/lablogs/",
        "courses_path"      => "/var/lilalo/courses/",
        "outpath"           => "/var/lilalo/out/",
        "path_web"          => "/var/www/l3",   # Путь к web-отчётам
        "path_share"        => "./share/",      # Путь к web-отчётам
    # Файлы
        "runfile"           => "lm.run",
        "logfile"           => "lm.log",
        "class"             => "class",         # Имя файла класса
        "class_suffix"      => ".xml",      # Cуффикс файла класса
        "classfile"         => "",
        "sshkey"            => "$ENV{HOME}/.ssh/id_dsa.pub",
        "lmssh"             => "./lm-ssh",
        "lminstall"         => "./lm-install",
        "ssh_user"          => "root",
        "l3scripts"         => "l3scripts",
        "cgi_path_info"     => "",
        "cgi2file"          => "",
        "year"              =>  "2006",
        "commands_to_show_at_a_go" => "100",
        "start_from_command" => "0",
    );
    sub read_config_file
    {
            my $config = $_[0];
            my $filename = $_[1];
            open(CONFIG, "$filename")
                    or return;
            while (<CONFIG>) {
            chomp;
                    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, $System_Config_File);
            read_config_file(\%file_config, $User_Config_File);
            GetOptions(\%argv_config, map "$_=s", keys %Config);
            %Config = (%Config, %file_config, %argv_config);
        for my $key (keys %Config) {
            utf8::decode($Config{$key});
        }
    }
    
    /home/su/.bashrc
    >
    # .bashrc
    # User specific aliases and functions
    export GREP_OPTIONS="--color=auto"
    # Source global definitions
    if [ -r /etc/bashrc ]; then
            . /etc/bashrc
    fi
    #[ $0 == l3script ] && . /home/su/.lilalo/l3bashrc && _l3_start
    #. ${user_home}/.lilalo/l3bashrc && _l3_start
    . ~/.lilalo/l3bashrc && _l3_start
    
    /usr/local/bin/l3-agent
    >
    #!/usr/bin/perl -w
    #
    # (c) Igor Chubin, igor@chub.in, 2004-2008
    #
    use strict;
    use POSIX;
    use Term::VT102;
    use Text::Iconv;
    use Time::Local 'timelocal_nocheck';
    use IO::Socket;
    use lib "/etc/lilalo";
    use l3config;
    our @Command_Lines;
    our @Command_Lines_Index;
    our %Diffs;
    our %Sessions;
    our %Script_Files;  # Информация о позициях в скрипт-файлах,
                        # до которых уже выполнен разбор
                        # и информация о времени модификации файла
                        #   $Script_Files{$file}->{size}
                        #   $Script_Files{$file}->{tell}
    our $Killed =0;     # В режиме демона -- процесс получил сигнал о завершении
    sub init_variables;
    sub main;
    sub load_diff_files;
    sub bind_diff;
    sub extract_commands_from_cline;
    sub load_command_lines;
    sub sort_command_lines;
    sub print_command_lines;
    sub printq;
    sub save_cache_stat;
    sub load_cache_stat;
    sub print_session;
    sub load_diff_files
    {
        my @pathes = @_;
        for my $path (@pathes) {
            my $template = "*.diff";
            my @files = <$path/$template>;
            my $i=0;
            for my $file (@files) {
                next if defined($Diffs{$file});
                my %diff;
    # Старый формат имени diff-файла
    # DEPRECATED
                if ($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{"uid"} = 0 if $path =~ m@/root/@;
                print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n";
                }
    # Новый формат имени diff-файла
                elsif ($file =~ m@.*/([^_]*)_([0-9]+)(.*)@) {
                    $diff{"local_session_id"} = $1;
                    $diff{"time"} = $2;
                    $diff{"filename"} = $3;
                    $diff{"filename"} =~ s@_@/@g;
                    $diff{"filename"} =~ s@//@_@g;
                    print "diff loaded: $diff{filename} (time=$diff{time},session=$diff{local_session_id})\n";
                }
                else {
                    next;
                }
    # Чтение и изменение кодировки содержимого diff-файла
                local $/;
                open (F, "$file")
                    or return "Can't open file $file ($_[0]) for reading";
                my $text = <F>;
                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;
                $diff{"path"}=$path;
                $diff{"bind_to"}="";
                $diff{"time_range"}=-1;
                $diff{"index"}=$i;
                $Diffs{$file} = \%diff;
                $i++;
            }
        }
    }
    sub bind_diff
    {
        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;
        if (defined($cl->{"diff"})) {
            print STDERR "Command ".$cl->{time}." is already bound";
            return;
        }
    # Загружаем новые diff-файлы
    # Это нужно делать непосредственно перед привязкой, поскольку diff'ы могли образоваться только что
        for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) {
            load_diff_files($lab_log);
        }
        my $diff_to_bind;
        for my $diff_key (keys %Diffs) {
            my $diff = $Diffs{$diff_key};
            next if ($diff->{"local_session_id"}
                    && $cl->{"local_session_id"}
                    && ($cl->{"local_session_id"} ne $diff->{"local_session_id"}));
            next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"}));
            my $dt;
            if (not $diff->{"time"}) {
                print STDERR "diff time is 0";
                print STDERR join(" ", keys(%$diff));
                print STDERR $diff->{text};
            }
            if (not $cl->{"time"}) {
                print STDERR "cl time is 0";
            }
            if ($diff->{"time"} && $cl->{"time"}) {
                $dt = $diff->{"time"} - $cl->{"time"}
            }
            else {
                $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec);
            }
            if ($dt >=0 && $dt < $min_dt && !$diff->{"bind_to"}) {
                 $min_dt = $dt;
                 $diff_to_bind = $diff_key;
            }
        }
        if ($diff_to_bind) {
            print "Approppriate diff found: dt=$min_dt\n";
            $Diffs{$diff_to_bind}->{"bind_to"}=$cl;
            $cl->{"diff"} = $diff_to_bind;
        }
        else {
            print STDERR "Diff not found\n";
            print STDERR "cl{time}",$cl->{time},"\n";
        }
    }
    sub extract_commands_from_cline
    # Разобрать командную строку $_[1] и возвратить хэш, содержащий
    # номер первого появление команды в строке:
    #   команда => первая позиция
    {
        my $cline = $_[0];
        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++;
            };
        }
        return %commands;
    }
    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/sx;
        my $cline_re2 = qr/$cline_re_base$/sx;
        my $cline_re_v2_base = qq'
                (
                v2[\#]                      # version
                ([0-9]+)[\#]                # history line number
                ([0-9]+)[\#]                # exitcode
                ([0-9]+)[\#]                # uid
                ([0-9]+)[\#]                # pid
                ([0-9]+)[\#]                # time
                (.*?)[\#]                   # pwd
                .\\[1024D.\\[K              # killing symbols
                (.*?([\$\#]\\s?))           # prompt
                (.*)                        # command line
                )
                ';
        my $cline_re_v2 = qr/$cline_re_v2_base/sx;
        my $cline_re2_v2 = qr/$cline_re_v2_base$/sx;
        my $cline_re_v3_base = qq'
                (
                v3[\#]                      # version
                .*
                )
                ';
        my $cline_re_v3 = qr/$cline_re_v3_base/sx;
        my $cline_re2_v3_base = qq'
                (
                v3[\#]                      # version
                ([0-9]+)[\#]                # history line number
                ([0-9]+)[\#]                # exitcode
                ([0-9]+)[\#]                # uid
                ([0-9]+)[\#]                # pid
                ([0-9]+)[\#]                # time
                (.*?)[\#]                   # pwd
                (.*?)[\#]                   # nonce
                (.*?([\$\#]\\s?))           # prompt
                (.*)                        # command line
                )
                ';
        my $cline_re2_v3 = qr/$cline_re2_v3_base$/sx;
        my %vt;     # Хэш виртуальных терминалов. По одному на каждый сеанс
        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 "Parsing lab scripts...\n" if $Config{"verbose"} =~ /y/;
        my $file;
        my $skip_info;
        my $commandlines_loaded =0;
        my $commandlines_processed =0;
        my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>;
        for $file (@lab_scripts){
            # Пропускаем файл, если он не изменялся со времени нашего предудущего прохода
            my $size = (stat($file))[7];
            next if ($Script_Files{$file} && $Script_Files{$file}->{size} && $Script_Files{$file}->{size} >= $size);
            my $local_session_id;
            # Начальное значение идентификатора текущего сеанса определяем из имени скрипта
            # Впоследствии оно может быть уточнено
            $file =~ m@.*/([^/]*)\.script$@;
            $local_session_id = $1;
            if (not defined($vt{$local_session_id})) {
                $vt{$local_session_id} = Term::VT102->new (
                                            'cols' => $Config{"terminal_width"},
                                            'rows' => $Config{"terminal_height"});
            }
            #Если файл только что появился,
            #пытаемся найти и загрузить информацию о соответствующей ему сессии
            if (!$Script_Files{$file}) {
                my $session_file = $file;
                $session_file =~ s/\.script/.info/;
                if (open(SESSION, $session_file)) {
                    local $/;
                    my $data = <SESSION>;
                    close(SESSION);
                    for my $session_data ($data =~ m@<session>(.*?)</session>@sg) {
                        my %session;
                        while ($session_data =~ m@<([^>]*?)>(.*?)</\1>@sg) {
                            $session{$1} = $2;
                        }
                        $local_session_id = $session{"local_session_id"} if $session{"local_session_id"};
                        $Sessions{$local_session_id}=\%session;
                    }
                    #Загруженную информацию сразу же отправляем в поток
                    print_session($Config{cache}, $local_session_id);
                }
                else {
                    die "can't open session file";
                }
            }
            open (FILE, "$file");
            binmode FILE;
            # Переходим к тому месту, где мы окончили разбор
            seek (FILE, $Script_Files{$file}->{tell}, 0) if $Script_Files{$file}->{tell};
            $Script_Files{$file}->{size} = $size;
            $Script_Files{$file}->{tell} = 0 unless $Script_Files{$file}->{tell};
            $file =~ m@.*/(.*?)-.*@;
            print "\n+- processing file $file\n|   "
                if $Config{"verbose"} =~/y/;
            my $tty = $1;
            my %cl;
            my $last_output_length=0;
            my $saved_output;
            while (<FILE>) {
                $commandlines_processed++;
                next if s/^Script started on.*?\n//s;
                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)//;
                    m/$cline_re2/gs;
                    $commandlines_loaded++;
                    $last_output_length=0;
                    # Previous command
                    my %last_cl = %cl;
                    my $this_line = $1;
                    my $err = $2 || "";
                    $cl{"local_session_id"} = $local_session_id;
                    # Parse new command
                    $cl{"uid"} = $3;
                    #$cl{"euid"} = $cl{"uid"};   # Если в команде обнаружится sudo, euid поменяем на 0
                    $cl{"pid"} = $4;
                    $cl{"day"} = $5;
                    $cl{"lab"} = $6;
                    $cl{"hour"} = $7;
                    $cl{"min"} = $8;
                    $cl{"sec"} = $9;
                    #$cl{"fullprompt"} = $10;
                    $cl{"prompt"} = $11;
                    $cl{"raw_cline"} = $12;
                    {
                    use bytes;
                    $cl{"raw_start"} = tell (FILE) - length($this_line);
                    $cl{"raw_output_start"} = tell FILE;
                    }
                    $cl{"raw_file"} = $file;
                    $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*$//;
                    $cl{"cline"} =~ s/.*?[\#\$]\s*//;
                    $cline_vt->reset();
                    my %commands = extract_commands_from_cline($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"};
                    if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) {
                        bind_diff(\%last_cl);
                    }
                    # Error code
                    $last_cl{"raw_end"} = $cl{"raw_start"};
                    $last_cl{"err"}=$err;
                    $last_cl{"err"}=130 if $err eq "^C";
                    # Output
                    if (!$last_cl{"suppress_output"} || $last_cl{"err"}) {
                        for (my $i=0; $i<$Config{"terminal_height"}; $i++) {
                            my $line= $vt{$local_session_id}->row_plaintext($i);
                            next if !defined ($line) ; #|| $line =~ /^\s*$/;
                            $line =~ s/\s*$//;
                            $line .= "\n" unless $line =~ /^\s*$/;
                            $last_cl{"output"} .= $line;
                        }
                    }
                    else {
                        $last_cl{"output"}= "";
                    }
                    $vt{$local_session_id}->reset();
                    # Save
                    if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) {
                        # Changing encoding
                        for (keys %last_cl) {
                            next if /raw/;
                            $last_cl{$_} = $converter->convert($last_cl{$_})
                                if ($Config{"encoding"} &&
                                $Config{"encoding"} !~ /^utf-8$/i);
                        }
                        push @Command_Lines, \%last_cl;
                        # Сохранение позиции в файле, до которой выполнен
                        # успешный разбор
                        $Script_Files{$file}->{tell} = $last_cl{raw_end};
                    }
                    next;
                }
                elsif (m/$cline_re_v2/ || m/$cline_re_v3/) {
    # Разбираем командную строку версии 2
                    my $before=$_;
                    s/.*\x0d(?!\x0a)//;
                    my $re;
                    if (m/$cline_re_v2/) {
                        $re=$cline_re2_v2;
                    }
                    else {
                        s/.\[1K.\[10D//gs;
                        $re=$cline_re2_v3;
                        print STDERR "... $_ ...\n";
                    }
                    m/$re/gs;
                    $commandlines_loaded++;
                    $last_output_length=0;
                    # Previous command
                    my %last_cl = %cl;
                    $cl{"local_session_id"} = $local_session_id;
                    # Parse new command
                    my $this_line = $1;
                    $cl{"history"}  = $2;
                    my $err         = $3;
                    $cl{"uid"}      = $4;
                    #$cl{"euid"}     = $cl{"uid"};   # Если в команде обнаружится sudo, euid поменяем на 0
                    $cl{"pid"}      = $5;
                    $cl{"time"}     = $6;
                    $cl{"pwd"}      = $7;
                    $cl{"nonce"}    = $8;
                    #$cl{"fullprompt"} = $8;
                    $cl{"prompt"}   = $10;
                    #$cl{"raw_cline"}= $10;
                    $cl{"raw_cline"}= $before;
                    {
                    use bytes;
                    $cl{"raw_start"} = tell (FILE) - length($before);
                    $cl{"raw_output_start"} = tell FILE;
                    }
                    $cl{"raw_file"} = $file;
                    $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*$//;
                    $cl{"cline"} =~ s/.*?[\#\$]\s*//;
                    $cline_vt->reset();
                    print STDERR "cline=".$cl{"cline"}."<<\n";
                    my %commands    = extract_commands_from_cline($cl{"cline"});
                    #$cl{"euid"}     = 0 if defined $commands{"sudo"};
                    my @comms       = sort { $commands{$a} cmp $commands{$b} } keys %commands;
                    $cl{"last_command"}
                                    = $comms[$#comms] || "";
                    print STDERR "last_command=".$cl{"last_command"}."<<\n";
                    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;
                    if ($Config{verbose} =~ /y/i) {
                        print "\n|   " if $commandlines_loaded % 5 == 1;
                        print " ",$cl{"last_command"};
                    }
                    if (defined($last_cl{time})
                        && grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) {
                           bind_diff(\%last_cl);
                    }
                    # Error code
                    $last_cl{"err"}=$err;
                    $last_cl{"raw_end"} = $cl{"raw_start"};
                    # Output
                    if (!$last_cl{"suppress_output"} || $last_cl{"err"}) {
                        $last_cl{"output"}=$saved_output;
                        for (my $i=0; $i<$Config{"terminal_height"}; $i++) {
                            my $line= $vt{$local_session_id}->row_plaintext($i);
                            next if !defined ($line) ; #|| $line =~ /^\s*$/;
                            $line =~ s/\s*$//;
                            $line .= "\n" unless $line =~ /^\s*$/;
                            $last_cl{"output"} .= $line;
                        }
                    }
                    else {
                        $last_cl{"output"}= "";
                    }
                    $vt{$local_session_id}->reset();
                    $saved_output="";
                    # Changing encoding
                    for (keys %last_cl) {
                        next if /raw/;
                        if ($Config{"encoding"} &&
                            $Config{"encoding"} !~ /^utf-8$/i) {
                            $last_cl{$_} = $converter->convert($last_cl{$_})
                        }
                    }
                    if (defined($last_cl{time})) {
                        print STDERR "push id=".$last_cl{time}."\n";
                        push @Command_Lines, \%last_cl;
                        # Сохранение позиции в файле, до которой выполнен
                        # успешный разбор
                        $Script_Files{$file}->{tell} = $last_cl{raw_end};
                    }
                    next;
                }
                if (($commandlines_processed%100) == 0) {
    # Каждые сто строк обнуляем терминал и переносим вывод из него в кэш
                    # Output
                    for (my $i=0; $i<$Config{"terminal_height"}; $i++) {
                        my $line= $vt{$local_session_id}->row_plaintext($i);
                        next if !defined ($line) ; #|| $line =~ /^\s*$/;
                        $line =~ s/\s*$//;
                        $line .= "\n" unless $line =~ /^\s*$/;
                        $saved_output .= $line;
                    }
                    $vt{$local_session_id}->reset();
                    $last_output_length=0;
                }
    # Иначе, это строка вывода
                $last_output_length+=length($_);
                #if (!$cl{"suppress_output"} || $last_output_length < 5000) {
                if ($last_output_length < 50000) {
                    $vt{$local_session_id}->process("$_"."\n")
                }
                else
                {
                    if (!$skip_info && defined($cl{last_command})) {
                        print "($cl{last_command})";
                        $skip_info = 1;
                    }
                }
            }
            close(FILE);
        }
        if ($Config{"verbose"} =~ /y/) {
            print "\n`- finished.\n" ;
            print "Lines loaded: $commandlines_processed\n";
            print "Command lines: $commandlines_loaded\n";
        }
    }
    sub sort_command_lines
    {
        print "Sorting command lines..." 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 {
               defined($Command_Lines[$index[$a]]->{"time"})
            && defined($Command_Lines[$index[$b]]->{"time"})
            ?  $Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"}
            :  defined($Command_Lines[$index[$a]]->{"day"})
               && defined($Command_Lines[$index[$b]]->{"day"})
               && defined($Command_Lines[$index[$a]]->{"hour"})
               && defined($Command_Lines[$index[$b]]->{"hour"})
               && defined($Command_Lines[$index[$a]]->{"min"})
               && defined($Command_Lines[$index[$b]]->{"min"})
               && defined($Command_Lines[$index[$a]]->{"sec"})
               && defined($Command_Lines[$index[$b]]->{"sec"})
               ?  $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"}
               :  0
        } @index;
        print "finished\n" if $Config{"verbose"} =~ /y/;
    }
    sub printq
    {
        my $TO = shift;
        my $text = join "", @_;
        $text =~ s/&/&amp;/g;
        $text =~ s/</&lt;/g;
        $text =~ s/>/&gt;/g;
        print $TO $text;
    }
    =cut
    Вывести результат обработки журнала.
    =cut
    sub print_command_lines
    {
        my $output_filename=$_[0];
        open(OUT, ">>", $output_filename)
            or die "Can't open $output_filename for writing\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 $output="";
            if (!grep ($_ eq $cl->{"last_command"}, @{$Config{"full_output_commands"}})
                && ($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 ($i=$start; $i<= $#lines; $i++) {
                    $output .= $lines[$i]."\n";
                }
            }
            else {
                # Full output
                $output .= $cl->{"output"};
            }
            # Совместимость с labmaker
            # Переводим в секунды Эпохи
            # В labmaker'е данные хранились в неудобной форме: hour, min, sec, day of year
            # Информация о годе отсутствовала
            # Её можно внести:
            # Декабрь 2004 год; остальные -- 2005 год.
            my $year = 2005;
            #$year = 2004 if ( $cl->{day} > 330 );
            $year = $Config{year} if $Config{year};
            # timelocal(            $sec,      $min,      $hour,      $mday,$mon,$year);
            $cl->{time} ||= timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year);
            # Начинаем вывод команды
            print OUT "<command>\n";
            print OUT "<l3cd>$Config{l3cd}</l3cd>\n" if $Config{"l3cd"};
            for my $element (qw(
                local_session_id
                history
                uid
                pid
                time
                pwd
                raw_start
                raw_output_start
                raw_end
                raw_file
                tty
                err
                last_command
                history
                nonce
                )) {
                next unless defined($cl->{"$element"});
                print OUT "<$element>".$cl->{$element}."</$element>\n";
            }
            for my $element (qw(
                prompt
                cline
                )) {
                next unless defined($cl->{"$element"});
                print OUT "<$element>";
                printq(\*OUT,$cl->{"$element"});
                print OUT "</$element>\n";
            }
                #note
                #note_title
            print OUT "<output>";
            printq(\*OUT,$output);
            print OUT "</output>\n";
            if ($cl->{"diff"}) {
                print OUT "<diff>";
                printq(\*OUT,${$Diffs{$cl->{"diff"}}}{"text"});
                print OUT "</diff>\n";
            }
            print OUT "</command>\n";
        }
        close(OUT);
    }
    sub print_session
    {
        my $output_filename = $_[0];
        my $local_session_id = $_[1];
        return if not defined($Sessions{$local_session_id});
        print "printing session info. session id = ".$local_session_id."\n"
            if $Config{verbose} =~ /y/;
        open(OUT, ">>", $output_filename)
            or die "Can't open $output_filename for writing\n";
        print OUT "<session>\n";
        print OUT "<l3cd>$Config{l3cd}</l3cd>\n" if $Config{"l3cd"};
        my %session = %{$Sessions{$local_session_id}};
        for my $key (keys %session) {
            print OUT "<$key>".$session{$key}."</$key>\n";
            print "         ".$key,"\n";
        }
        print OUT "</session>\n";
        close(OUT);
    }
    sub send_cache
    {
        # Если в кэше что-то накопилось,
        # попытаемся отправить это на сервер
        #
        my $cache_was_sent=0;
        if (open(CACHE, $Config{cache})) {
            local $/;
            my $cache = <CACHE>;
            close(CACHE);
            my $socket = IO::Socket::INET->new(
                                PeerAddr => $Config{backend_address},
                                PeerPort => $Config{backend_port},
                                proto   => "tcp",
                                Type    => SOCK_STREAM
                            );
            if ($socket) {
                print $socket $cache;
                close($socket);
                $cache_was_sent = 1;
            }
        }
        return $cache_was_sent;
    }
    sub save_cache_stat
    {
        open (CACHE, ">$Config{cache_stat}");
        for my $f (keys %Script_Files) {
            print CACHE "$f\t",$Script_Files{$f}->{size},"\t",$Script_Files{$f}->{tell},"\n";
        }
        close(CACHE);
    }
    sub load_cache_stat
    {
        if (open (CACHE, "$Config{cache_stat}")) {
            while(<CACHE>) {
                chomp;
                my ($f, $size, $tell) = split /\t/;
                $Script_Files{$f}->{size} = $size;
                $Script_Files{$f}->{tell} = $tell;
            }
            close(CACHE);
        };
    }
    main();
    sub process_was_killed
    {
        $Killed = 1;
    }
    sub reload
    {
        init_config;
    }
    sub main
    {
        $| = 1;
        init_variables();
        init_config();
        if ($Config{"mode"} ne "daemon") {
    #    В нормальном режиме работы нужно
    #    считать скрипты, обработать их и записать
    #    результат выполнения в результирующий файл.
    #    После этого завершить работу.
    # Очистим кэш-файл, если он существовал
            if (open (CACHE, ">", $Config{"cache"})) {
                close(CACHE);
            };
            load_command_lines($Config{"input"}, $Config{"input_mask"});
            sort_command_lines;
            #process_command_lines;
            print_command_lines($Config{"cache"});
        }
        else {
            if (open(PIDFILE, $Config{agent_pidfile})) {
                my $pid = <PIDFILE>;
                close(PIDFILE);
                if ($^O eq 'linux' && $pid &&(! -e "/proc/$pid" || !`grep $Config{"l3-agent"} /proc/$pid/cmdline && grep "uid:.*\b$<\b" /proc/$pid/status`)) {
                    print "Removing stale pidfile\n";
                    unlink $Config{agent_pidfile}
                        or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!";
                }
                elsif ($^O eq 'freebsd' && defined($pid) && $pid ne "" && not `ps axo uid,pid,command | grep '$< $pid $Config{"l3-agent"}' | grep -v grep 2> /dev/null`) {
                    print "Removing stale pidfile\n";
                    unlink $Config{agent_pidfile}
                        or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!";
                }
                elsif ($^O eq 'linux' || $^O eq 'freebsd' ) {
                    print "l3-agent is already running: pid=$pid; pidfile=$Config{agent_pidfile}\n";
                    exit(0);
                }
                else {
                    print "Unknown operating system";
                    exit(0);
                }
            }
            if ($Config{detach} =~ /^y/i) {
                #$Config{verbose} = "no";
                my $pid = fork;
                exit if $pid;
                die "Couldn't fork: $!" unless defined ($pid);
                open(PIDFILE, ">", $Config{agent_pidfile})
                    or die "Can't open pidfile ". $Config{agent_pidfile}. " for wrting: $!";
                print PIDFILE $$;
                close(PIDFILE);
                for my $handle (*STDIN, *STDOUT, *STDERR) {
                    open ($handle, "+<", "/dev/null")
                        or die "can't reopen $handle to /dev/null: $!"
                }
                POSIX::setsid()
                    or die "Can't start a new session: $!";
                $0 = $Config{"l3-agent"};
                $SIG{INT} = $SIG{TERM} = \&process_was_killed;
                $SIG{HUP} = \&reload;
            }
            while (not $Killed) {
                @Command_Lines = ();
                @Command_Lines_Index = ();
                load_cache_stat();
                load_command_lines($Config{"input"}, $Config{"input_mask"});
                if (@Command_Lines) {
                    sort_command_lines;
                    #process_command_lines;
                    print_command_lines($Config{"cache"});
                }
                save_cache_stat();
                if (-e $Config{cache} && (stat($Config{cache}))[7]) {
                    send_cache() && unlink($Config{cache});
                }
                sleep($Config{"daemon_sleep_interval"} || 1);
            }
            unlink $Config{agent_pidfile};
        }
    }
    sub init_variables
    {
    }
    
    268158378436214913-1315560387.script
    >
    Скрипт запущен Птн 09 Сен 2011 13:26:27
    
    l3
    >
    l3-agent     l3-cgi       l3config.pm  l3prompt     l3-upload
    l3-backend   l3-cgi-lite  l3files/     l3-report
    l3bashrc     l3-config    l3-frontend  l3scripts
    
    l3-agent.pid
    >
    l3-backend
    >
    #!/usr/bin/perl
    use strict;
    use lib '/etc/lilalo/';
    use l3config;
    use IO::Socket;
    use POSIX qw(:sys_wait_h);
    sub main;
    main();
    sub REAPER {
            1 until (-1 == waitpid(-1, WNOHANG));
            $SIG{CHLD} = \&REAPER;
    }
    sub process_was_killed
    {
            # Здесь должна быть красивая процедура
            # завершения демона
            unlink $Config{backend_pidfile};
            exit(0);
    }
    sub main {
            init_config();
            # Проверяем, возможно демон уже запущен
            # Если он работает, просто завершаемся
            if (open(PIDFILE, $Config{backend_pidfile})) {
                    my $pid = <PIDFILE>;
                    close(PIDFILE);
                    if ( ! -e "/proc/$pid" || !`grep $Config{"l3-backend"} /proc/$pid/cmdline && grep "uid:.*\b$<\b" /proc/$pid/status`) {
                            print "Removing stale pidfile\n";
                            unlink $Config{backend_pidfile}
                                    or die "Can't remove stale pidfile ". $Config{backend_pidfile}. " : $!";
                    }
                    else {
                            print "l3-backend is already running\n";
                            exit(0);
                    }
            }
            # Уходим в background, если необходимо
            if ($Config{detach} =~ /^y/i) {
                    #$Config{verbose} = "no";
                    my $pid = fork;
                    exit if $pid;
                    die "Couldn't fork: $!" unless defined ($pid);
                    open(PIDFILE, ">", $Config{backend_pidfile})
                            or die "Can't open pidfile ". $Config{backend_pidfile}. " for wrting: $!";
                    print PIDFILE $$;
                    close(PIDFILE);
                    for my $handle (*STDIN, *STDOUT, *STDERR) {
                            open ($handle, "+<", "/dev/null")
                                    or die "can't reopen $handle to /dev/null: $!"
                    }
                    POSIX::setsid()
                            or die "Can't start a new session: $!";
                    $0 = $Config{"l3-backend"};
                    $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&process_was_killed;
            }
            # Открываем сетевой сокет и слушаем
            my $server = IO::Socket::INET->new(
                            LocalPort       => $Config{backend_port},
                            Type            => SOCK_STREAM,
                            Reuse           => 1,
                            Listen          => 10 );
            if (!$server) {
                    die "Couldn't bind to socket ".$Config{backend_port}."\n";
            }
            $SIG{CHLD} = 'IGNORE';
            # При получении новых соединенений,
            # порождаем дочерние процессы
            while (my $client = $server->accept()) {
                    my $pid;
                    next if $pid = fork;
                    die "fork: $!" unless defined $pid;
                    # Это наш ответвлённый клиент
                    close($server);
            my $saved_data = "";
                    # Считываем данные и передаём их в точку получения
                    open(OUT, ">>", $Config{"backend_datafile"});
                    select OUT; $|=1;
                    while(<$client>) {
                            print OUT $_;
                $saved_data .= $_;
                    }
                    close(OUT);
                #open(LOG, ">>", "/tmp/l3-backend.log");
                #print LOG "Saved data:$saved_data\n";
                #close(LOG);
            while ($saved_data =~ m@<(session|command)>(.*)</\1>@sg) {
                #open(LOG, ">>", "/tmp/l3-backend.log");
                #print LOG "Found element $1\n";
                #close(LOG);
                my $element_name = $1;
                my $element = $2;
                if ($element =~ m@<l3cd>(.*?)</l3cd>@msg) {
    # Обнаружен элемент l3cd
    # Информация должна быть сохранена в соответствующий каталог
                    my $l3cd = $1;
    # Путь l3cd должен быть не пуст,
    # и в нём могут быть только символы латинского алфавита, цифры и знаки _ и -
                    if ($l3cd && $l3cd =~ /^[a-zA-Z_\/0-9-]*/) {
                        system("mkdir -m 770 -p $Config{backend_datadir}/$l3cd");
                        if (open(OUT, ">>", $Config{"backend_datadir"}."/$l3cd/data.xml")) {
                            print OUT "<$element_name>".$element."</$element_name>";
                            close(OUT);
                        };
                    }
                }
            }
            }
            continue {
                    # Наш родитель
                    close ($client);
            }
    }
    
    l3-config
    >
    #!/usr/bin/perl
    use strict;
    use lib '/etc/lilalo/';
    use l3config;
    print $Config{$ARGV[0]}."\n";
    
    l3-frontend
    >
    #!/usr/bin/perl -w
    use POSIX qw(strftime);
    use lib '/etc/lilalo';
    use l3config;
    use utf8;
    our @Command_Lines;
    our @Command_Lines_Index;
    our %Commands_Description;
    our %Args_Description;
    our %Sessions;
    our $debug_output="";      # Используйте эту переменную, если нужно передать отладочную информацию
    our %filter;
    our $filter_url;
    sub init_filter;
    our %Files;
    # vvv Инициализация переменных выполняется процедурой init_variables
    our @Day_Name;
    our @Month_Name;
    our @Of_Month_Name;
    our %Search_Machines;
    our %Elements_Visibility;
    # ^^^
    our $First_Command=$0;
    our $Last_Command=40;
    our %Stat;
    our %frequency_of_command; # Сколько раз в журнале встречается какая команда
    our $table_number=1;
    our %tigra_hints;
    my %mywi_cache_for;         # Кэш для экономии обращений к mywi
    sub count_frequency_of_commands;
    sub make_comment;
    sub make_new_entries_table;
    sub load_command_lines_from_xml;
    sub load_sessions_from_xml;
    sub sort_command_lines;
    sub process_command_lines;
    sub init_variables;
    sub main;
    sub collapse_list($);
    sub minutes_passed;
    sub print_all_txt;
    sub print_all_html;
    sub print_edit_all_html;
    sub print_command_lines_html;
    sub print_command_lines_txt;
    sub print_files_html;
    sub print_stat_html;
    sub print_header_html;
    sub print_footer_html;
    sub tigra_hints_generate;
    #### mywi
    #
    sub mywi_init;
    sub load_mywitxt;
    sub mywi_process_query($);
    #
    sub add_to_log($$);
    sub parse_query;
    sub search_in_txt;
    sub add_to_log($$);
    sub mywi_guess($);
    #
    main();
    sub main
    {
        $| = 1;
        init_variables();
        init_config();
        $Config{frontend_ico_path}=$Config{frontend_css};
        $Config{frontend_ico_path}=~s@/[^/]*$@@;
        init_filter();
        mywi_init();
        load_command_lines_from_xml($Config{"backend_datafile"});
        load_sessions_from_xml($Config{"backend_datafile"});
        sort_command_lines;
        process_command_lines;
        if (defined($filter{action}) && $filter{action} eq "edit") {
            print_edit_all_html($Config{"output"});
        }
        else {
            print_all_html($Config{"output"});
        }
    }
    sub init_filter
    {
        if ($Config{filter}) {
            # Инициализация фильтра
            for (split /&/,$Config{filter}) {
                my ($var, $val) = split /=/;
                $filter{$var} = $val || "";
            }
        }
        $filter_url = join ("&", map("$_=$filter{$_}", keys %filter));
    }
    # extract_from_cline
    # In:   $what       = commands | args
    # Out:  return      ссылка на хэш, содержащий результаты разбора
    #                   команда => позиция
    # Разобрать командную строку $_[1] и возвратить хэш, содержащий
    # номер первого появление команды в строке:
    #   команда => первая позиция
    sub extract_from_cline
    {
        my $what = $_[0];
        my $cline = $_[1];
        my @lists = split /\;/, $cline;
        my @command_lines = ();
        for my $command_list (@lists) {
            push(@command_lines, split(/\|/, $command_list));
        }
        my %position_of_command;
        my %position_of_arg;
        my $i=0;
        for my $command_line (@command_lines) {
            $command_line =~ s@^\s*@@;
            $command_line =~ /\s*(\S+)\s*(.*)/;
            if ($1 && $1 eq "sudo" ) {
                $position_of_command{"$1"}=$i++;
                $command_line =~ s/\s*sudo\s+//;
            }
            if ($command_line !~ m@^\s*\S*/etc/@) {
                $command_line =~ s@^\s*\S+/@@;
            }
            $command_line =~ /\s*(\S+)\s*(.*)/;
            my $command = $1;
            my $args = $2;
            if ($command && !defined $position_of_command{"$command"}) {
                    $position_of_command{"$command"}=$i++;
            };
            if ($args) {
                my @args = split (/\s+/, $args);
                for my $a (@args) {
                    $position_of_arg{"$a"}=$i++
                        if !defined $position_of_arg{"$a"};
                };
            }
        }
        if ($what eq "commands") {
            return \%position_of_command;
        } else {
            return \%position_of_arg;
        }
    }
    sub mywrap($)
    {
    return '<div class="t"><div class="b"><div class="l"><div class="r"><div class="bl"><div class="br"><div class="tl"><div class="tr">'.$_[0].
    '</div></div></div></div></div></div></div></div>';
    }
    sub tigra_hints_generate
    {
        my $tigra_hints_items="";
        for my $hint_id (keys %tigra_hints) {
            $tigra_hints{$hint_id} =~ s@\n@<br/>@gs;
            $tigra_hints{$hint_id} =~ s@ - @ — @gs;
            $tigra_hints{$hint_id} =~ s@'@\\'@gs;
    #        $tigra_hints_items .= "'$hint_id' : mywrap('".$tigra_hints{$hint_id}."'),";
            $tigra_hints_items .= "'$hint_id' : '".mywrap($tigra_hints{$hint_id})."',";
        }
        $tigra_hints_items =~ s/,$//;
        return <<TIGRA;
    var HINTS_CFG = {
            'top'        : 5, // a vertical offset of a hint from mouse pointer
            'left'       : 5, // a horizontal offset of a hint from mouse pointer
            'css'        : 'hintsClass', // a style class name for all hints, TD object
            'show_delay' : 500, // a delay between object mouseover and hint appearing
            'hide_delay' : 2000, // a delay between hint appearing and hint hiding
            'wise'       : true,
            'follow'     : true,
            'z-index'    : 0 // a z-index for all hint layers
    },
    HINTS_CFG_NEW = {
        'wise'       : true, // don't go off screen, don't overlap the object in the document
        'margin'     : 10, // minimum allowed distance between the hint and the window edge (negative values accepted)
        'gap'        : 20, // minimum allowed distance between the hint and the origin (negative values accepted)
        'align'      : 'bctl', // align of the hint and the origin (by first letters origin's top|middle|bottom left|center|right to hint's top|middle|bottom left|center|right)
        'css'        : 'hintsClass', // a style class name for all hints, applied to DIV element (see style section in the header of the document)
        'show_delay' : 0, // a delay between initiating event (mouseover for example) and hint appearing
        'hide_delay' : 200, // a delay between closing event (mouseout for example) and hint disappearing
        'follow'     : true, // hint follows the mouse as it moves
        'z-index'    : 100, // a z-index for all hint layers
        'IEfix'      : false, // fix IE problem with windowed controls visible through hints (activate if select boxes are visible through the hints)
        'IEtrans'    : ['blendTrans(DURATION=.3)', null], // [show transition, hide transition] - nice transition effects, only work in IE5+
        'opacity'    : 90 // opacity of the hint in %%
    },
    HINTS_ITEMS = {
        $tigra_hints_items
    };
    var myHint = new THints (HINTS_CFG, HINTS_ITEMS);
    function mywrap (s_) {
    return '<div class="t"><div class="b"><div class="l"><div class="r"><div class="bl"><div class="br"><div class="tl"><div class="tr">'+s_+
    '</div></div></div></div></div></div></div></div>';
    }
    TIGRA
    $a=<<TIGRA;
    TIGRA
    }
    sub count_frequency_of_commands
    {
        my $cline = $_[0];
        my @commands = keys %{extract_from_cline("commands", $cline)};
        for my $command (@commands) {
            $frequency_of_command{$command}++;
        }
    }
    sub make_comment
    {
        my $cline = $_[0];
        #my $files = $_[1];
        my @comments;
        my @commands = keys %{extract_from_cline("commands", $cline)};
        my @args = keys %{extract_from_cline("args", $cline)};
        return if (!@commands && !@args);
        #return "commands=".join(" ",@commands)."; files=".join(" ",@files);
        # Commands
        for my $command (@commands) {
            $command =~ s/'//g;
            #$frequency_of_command{$command}++;
            if (!$Commands_Description{$command}) {
                $mywi_cache_for{$command} ||= mywi_process_query($command) || "";
                my $mywi = join ("\n", grep(/\([18]|sh|script\)/, split(/\n/, $mywi_cache_for{$command})));
                $mywi =~ s/\s+/ /;
                if ($mywi !~ /^\s*$/) {
                    $Commands_Description{$command} = $mywi;
                }
                else {
                    next;
                }
            }
            push @comments, $Commands_Description{$command};
        }
        return join("&#10;\n", @comments);
        # Files
        for my $arg (@args) {
            $arg =~ s/'//g;
            if (!$Args_Description{$arg}) {
                my $mywi;
                $mywi = mywi_client ($arg);
                $mywi = join ("\n", grep(/\([5]\)/, split(/\n/, $mywi)));
                $mywi =~ s/\s+/ /;
                if ($mywi !~ /^\s*$/) {
                    $Args_Description{$arg} = $mywi;
                }
                else {
                    next;
                }
            }
            push @comments, $Args_Description{$arg};
        }
    }
    =cut
    Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта
    из XML-документа в переменную @Command_Lines
    # In:       $datafile           имя файла
    # Out:      @CommandLines       загруженные командные строки
    Предупреждение!
    Процедура не в состоянии обрабатывать XML-документ любой структуры.
    В действительности файл cache из которого загружаются данные
    просто напоминает XML с виду.
    =cut
    sub load_command_lines_from_xml
    {
        my $datafile = $_[0];
        open (CLASS, $datafile)
            or die "Can't open file with xml lablog ",$datafile,"\n";
        local $/;
        binmode CLASS, ":utf8";
        $data = <CLASS>;
        close(CLASS);
        for $command ($data =~ m@<command>(.*?)</command>@sg) {
            my %cl;
            while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) {
                $cl{$1} = $2;
            }
            push @Command_Lines, \%cl;
        }
    }
    sub load_sessions_from_xml
    {
        my $datafile = $_[0];
        open (CLASS,  $datafile)
            or die "Can't open file with xml lablog ",$datafile,"\n";
        local $/;
        binmode CLASS, ":utf8";
        my $data = <CLASS>;
        close(CLASS);
        my $i=0;
        for my $session ($data =~ m@<session>(.*?)</session>@msg) {
            my %session_hash;
            while ($session =~ m@<([^>]*?)>(.*?)</\1>@sg) {
                $session_hash{$1} = $2;
            }
            $Sessions{$session_hash{local_session_id}} = \%session_hash;
        }
    }
    # sort_command_lines
    # In:   @Command_Lines
    # Out:  @Command_Lies_Index
    sub sort_command_lines
    {
        my @index;
        for (my $i=0;$i<=$#Command_Lines;$i++) {
            $index[$i]=$i;
        }
        @Command_Lines_Index = sort {
            $Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"}
        } @index;
    }
    ##################
    # process_command_lines
    #
    # Обрабатываются командные строки @Command_Lines
    # Для каждой строки определяется:
    #   class   класс
    #   note    комментарий
    #
    # In:        @Command_Lines_Index
    # In-Out:    @Command_Lines
    sub process_command_lines
    {
        my $current_command=0;
        my $prev_i;
        my $tab_seq =0 ; # номер команды в последовательности tab-completion
                         # отличен от нуля только для тех последовательностей,
                         # где постоянно нажимается клавиша tab
    COMMAND_LINE_PROCESSING:
        for my $i (@Command_Lines_Index) {
            $current_command++;
            next if $current_command < $Config{"start_from_command"};
            last if $current_command > $Config{"start_from_command"} + $Config{"commands_to_show_at_a_go"};
            my $cl = \$Command_Lines[$i];
            # Запоминаем предыщуюу команду
            # Она нам потребуется, в частности, для ввода tab_seq рпи обработке tab_completion
            my $prev_cl;
            $prev_cl = \$Command_Lines[$prev_i] if defined($prev_i);
            $prev_i = $i;
            next if !$cl;
            for my $filter_key (keys %filter) {
                next COMMAND_LINE_PROCESSING
                    if defined($$cl->{local_session_id})
                    && defined($Sessions{$$cl->{local_session_id}}->{$filter_key})
                    && $Sessions{$$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
            }
            $$cl->{id} = $$cl->{"time"};
            $$cl->{err} ||=0;
            # Класс команды
            $$cl->{"class"} =   $$cl->{"err"} eq 130 ?  "interrupted"
                            :   $$cl->{"err"} eq 127 ?  "mistyped"
                            :   $$cl->{"err"}        ?  "wrong"
                            :                           "normal";
            if ($$cl->{"cline"} &&
                $$cl->{"cline"} =~ /[^|`]\s*sudo/
                || $$cl->{"uid"} eq 0) {
                $$cl->{"class"}.="_root";
            }
            my $hint;
            count_frequency_of_commands($$cl->{"cline"});
            $hint = make_comment($$cl->{"cline"});
            if ($hint) {
                $$cl->{hint} = $hint;
            }
            $tigra_hints{$$cl->{"time"}} = $hint;
            #$$cl->{hint}="";
    # Выводим <head_lines> верхних строк
    # и <tail_lines> нижних строк,
    # если эти параметры существуют
            my $output="";
            if ($$cl->{"last_command"} eq "cat" && !$$cl->{"err"} && !($$cl->{"cline"} =~ /</)) {
                my $filename = $$cl->{"cline"};
                $filename =~ s/.*\s+(\S+)\s*$/$1/;
                $Files{$filename}->{"content"} = $$cl->{"output"};
               $Files{$filename}->{"source_command_id"} = $$cl->{"id"}
            }
            my @lines = split '\n', $$cl->{"output"};
            if ((
                 $Config{"head_lines"}
                 || $Config{"tail_lines"}
                 )
                 && $#lines >  $Config{"head_lines"} + $Config{"tail_lines"} ) {
    #
                for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
                    $output .= $lines[$i]."\n";
                }
                $output .= $Config{"skip_text"}."\n";
                my $start_line=$#lines-$Config{"tail_lines"}+1;
                for (my $i=$start_line; $i<= $#lines; $i++) {
                    $output .= $lines[$i]."\n";
                }
            }
            else {
               $output = $$cl->{"output"};
            }
            $$cl->{short_output} = $output;
    # Обработка команд с одинаковым временем
    # Скорее всего они набраны с помощью tab-completion
            if (defined($prev_cl)) {
               if ($$prev_cl->{time} == $$cl->{time} && $$prev_cl->{nonce} == $$cl->{nonce}) {
                $tab_seq++;
               }
               else {
                $tab_seq=0;
               };
               $$prev_cl->{tab_seq}=$tab_seq;
    # Обработка команд с одинаковым номером в истории
    # Скорее всего они набраны с помощью Ctrl-C
               #if ($$prev_cl->{history} == $$cl->{history}) {
               # $$prev_cl->{break}=1;
               #}
            }
    #Обработка пометок
    #  Если несколько пометок (notes) идут подряд,
    #  они все объединяются
            if ($$cl->{cline} =~ /l3shot/) {
                    if ($$cl->{output} =~ m@Screenshot is written to.*/(.*)\.xwd@) {
                        $$cl->{screenshot}="$1".$Config{l3shot_suffix};
                    }
            }
            if ($$cl->{cline} =~ /l3upload/) {
                    if ($$cl->{output} =~ m@Uploaded file name is (.*)@) {
                        $$cl->{screenshot}="$1";
                    }
            }
            if ($$cl->{cline}=~ m@cat[^#]*#([\^=v])\s*(.*)@) {
                my $note_operator = $1;
                my $note_title = $2;
                if ($note_operator eq "=") {
                    $$cl->{"class"} = "note";
                    $$cl->{"note"} = $$cl->{"output"};
                    $$cl->{"note_title"} = $2;
                }
                else {
                    my $j = $i;
                    if ($note_operator eq "^") {
                        $j--;
                        $j-- while ($j >=0  && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
                    }
                    elsif ($note_operator eq "v") {
                        $j++;
                        $j++ while ($j <= @Command_Lines  && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
                    }
                    $Command_Lines[$j]->{note_title}=$note_title;
                    $Command_Lines[$j]->{note}.=$$cl->{output};
                    $$cl=0;
                }
            }
            elsif ($$cl->{cline}=~ /#([\^=v])(.*)/) {
                my $note_operator = $1;
                my $note_text = $2;
                if ($note_operator eq "=") {
                    $$cl->{"class"} = "note";
                    $$cl->{"note"} = $note_text;
                }
                else {
                    my $j=$i;
                    if ($note_operator eq "^") {
                        $j--;
                        $j-- while ($j >=0  && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
                    }
                    elsif ($note_operator eq "v") {
                        $j++;
                        $j++ while ($j <= @Command_Lines  && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]);
                    }
                    $Command_Lines[$j]->{note}.="$note_text\n";
                    $$cl=0;
                }
            }
            if ($$cl->{"class"} eq "note") {
                    my $note_html = $$cl->{note};
                    $note_html = join ("\n", map ("<p>$_</p>", split (/-\n/, $note_html)));
                    $note_html =~ s@(http:[a-zA-Z.0-9/?\_%-]*)@<a href='$1'>$1</a>@g;
                    $note_html =~ s@(www\.[a-zA-Z.0-9/?\_%-]*)@<a href='$1'>$1</a>@g;
                    $$cl->{"note_html"} = $note_html;
            }
        }
    }
    =cut
    Процедура print_command_lines выводит HTML-представление
    разобранного lab-скрипта.
    Разобранный lab-скрипт должен находиться в массиве @Command_Lines
    =cut
    sub print_command_lines_html
    {
        my @toc;                # Оглавление
        my $note_number=0;
        my $result = q();
        my $this_day_resut = q();
        my $cl;
        my $last_tty="";
        my $last_session="";
        my $last_day=q();
        my $last_wday=q();
        my $first_command_of_the_day_unix_time=q();
        my $human_readable_time=q();
        my $in_range=0;
        my $current_command=0;
        my @known_commands;
        $Stat{LastCommand}   ||= 0;
        $Stat{TotalCommands} ||= 0;
        $Stat{ErrorCommands} ||= 0;
        $Stat{MistypedCommands} ||= 0;
        my %new_entries_of = (
            "1 1"     =>   "программы пользователя",
            "2 8"     =>   "программы администратора",
            "3 sh"    =>   "команды интерпретатора",
            "4 script"=>   "скрипты",
        );
    COMMAND_LINE:
        for my $k (@Command_Lines_Index) {
            my $cl=$Command_Lines[$Command_Lines_Index[$current_command++]];
            next unless $cl;
            next if $current_command < $Config{"start_from_command"};
            last if $current_command > $Config{"start_from_command"} + $Config{"commands_to_show_at_a_go"};
    # Пропускаем строки, которые противоречат фильтру
    # Если у нас недостаточно информации о том, подходит строка под  фильтр или нет,
    # мы её выводим
            for my $filter_key (keys %filter) {
                next COMMAND_LINE
                    if defined($cl->{local_session_id})
                    && defined($Sessions{$cl->{local_session_id}}->{$filter_key})
                    && $Sessions{$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
            }
    # Набираем статистику
    # Хэш %Stat
            $Stat{FirstCommand} = $cl->{time} unless $Stat{FirstCommand};
            if ($cl->{time} - $Stat{LastCommand} < $Config{stat_inactivity_interval}) {
                $Stat{TotalTime} += $cl->{time} - $Stat{LastCommand}
            }
            my $seconds_since_last_command = $cl->{time} - $Stat{LastCommand};
            if ($Stat{LastCommand} > $cl->{time}) {
                   $result .= "Время идёт вспять<br/>";
            };
            $Stat{LastCommand} = $cl->{time};
            $Stat{TotalCommands}++;
    # Пропускаем строки, выходящие за границу "signature",
    # при условии, что границы указаны
    # Пропускаем неправильные/прерванные/другие команды
            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);
    #
    ##
    ## Начинается собственно вывод
    ##
    #
    ### Сначала обрабатываем границы разделов
    ### Если тип команды "note", это граница
            if ($cl->{class} eq "note") {
                $this_day_result .= "<tr><td colspan='6'>"
                                 .  "<h4 id='note$note_number'>".$cl->{note_title}."</h4>" if $cl->{note_title}
                                 .  "".$cl->{note_html}."<p/><p/></td></tr>";
                if ($cl->{note_title}) {
                    push @{$toc[@toc]},"<a href='#note$note_number'>".$cl->{note_title}."</a>";
                    $note_number++;
                }
                next;
            }
            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->{"class"};
            $Stat{ErrorCommands}++          if $class =~ /wrong/;
            $Stat{MistypedCommands}++       if $class =~ /mistype/;
    # DAY CHANGE
            if ( $last_day ne $day) {
                $prev_unix_time=$first_command_of_the_day_unix_time;
                $first_command_of_the_day_unix_time = $cl->{time};
                $human_readable_time = strftime "%D", localtime($prev_unix_time);
                if ($last_day) {
    # Вычисляем разность множеств.
    # Что-то вроде этого, если бы так можно было писать:
    #   @new_commands = keys %frequency_of_command - @known_commands;
    # Выводим предыдущий день
                    $result .= "<h3 id='day_on_sec_$prev_unix_time'>".$Day_Name[$last_wday]." ($human_readable_time)</h3>";
                    for my $entry_class (sort keys %new_entries_of) {
                        my $table_caption = "Таблица ".$table_number++.".".$Day_Name[$last_wday]
                                            .". Новые ".$new_entries_of{$entry_class};
                        my $new_commands_section = make_new_entries_table(
                                                    $table_caption,
                                                    $entry_class=~/[0-9]+\s+(.*)/,
                                                    \@known_commands);
                    }
                    @known_commands = keys %frequency_of_command;
                    $result .= $this_day_result;
                }
    # Добавляем текущий день в оглавление
                $human_readable_time = strftime "%D", localtime($first_command_of_the_day_unix_time);
                push @toc, "<a href='#day_on_sec_$first_command_of_the_day_unix_time'>".$Day_Name[$wday]." ($human_readable_time)</a>\n";
                $last_day=$day;
                $last_wday=$wday;
                $this_day_result = q();
            }
            else {
                $this_day_result .= minutes_passed($seconds_since_last_command);
            }
            $this_day_result .= "<div class='command' id='command:".$cl->{"id"}."' >\n";
    # CONSOLE CHANGE
            if ($cl->{"tty"} && $last_tty ne $cl->{"tty"} && 0) {
                my $tty = $cl->{"tty"};
                $this_day_result .= "<div class='ttychange'>"
                                    . $tty
                                    ."</div>";
                $last_tty=$cl->{"tty"};
            }
    # Session change
            if ( $last_session ne $cl->{"local_session_id"}) {
                my $tty;
                if (defined $Sessions{$cl->{"local_session_id"}}->{"tty"}) {
                    $this_day_result .= "<div class='ttychange'><a href='?local_session_id=".$cl->{"local_session_id"}."'>"
                                    . $Sessions{$cl->{"local_session_id"}}->{"tty"}
                                    ."</a></div>";
                }
                $last_session=$cl->{"local_session_id"};
            }
    # TIME
            if ($Config{"show_time"} =~ /^y/i) {
                $this_day_result .= "<div class='time'>$hour:$min:$sec</div>"
            }
    # COMMAND
            my $cline;
            $prompt_hint = join ("&#10;",
                             map("$_=$cl->{$_}",
                               grep (!/^(output|short_output|diff)$/,
                                 sort(keys(%{$cl})))));
            $cline = "<span title='$prompt_hint'>".$cl->{"prompt"}."</span>"
                    ."<span onmouseover=\"myHint.show('".$cl->{time}."')\" onmouseout=\"myHint.hide()\">".$cl->{"cline"}."</span>";
            $cline =~ s/\n//;
            if ($cl->{"hint"}) {
    #            $cline = "<span title='$cl->{hint}' class='with_hint'>$cline</span>" ;
                $cline = "<span class='with_hint'>$cline</span>" ;
            }
            else {
                $cline = "<span class='without_hint'>$cline</span>";
            }
            $this_day_result .= "<DIV class='fixed_div'><table cellpadding='0' cellspacing='0'><tr><td>\n<div class='cblock_$cl->{class}'>\n";
            $this_day_result .= "<div class='cline'>" . $cline ;      #cline
            $this_day_result .= "<span title='Код завершения ".$cl->{"err"}."'>\n"
                             .  "<img src='".$Config{frontend_ico_path}."/error.png'/>\n"
                             .  "</span>\n" if ($cl->{"err"} and not $cl->{tab_seq} and not $cl->{break});
            $this_day_result .= "<span title='Tab completion ".$cl->{tab_seq}."'>\n"
                             .  "<img src='".$Config{frontend_ico_path}."/tab.png'/>\n"
                             .  "</span>\n" if $cl->{tab_seq};
            $this_day_result .= "<span title='Ctrl-C pressed'>\n"
                             .  "<img src='".$Config{frontend_ico_path}."/break.png'/>\n"
                             .  "</span>\n" if ($cl->{break} and not $cl->{tab_seq});
            $this_day_result .= "</div>\n";                             #cline
    # OUTPUT
            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"}})
                )) {
                $this_day_result .= "<pre class='output'>\n" . $cl->{short_output} . "</pre>\n";
            }
    # DIFF
            $this_day_result .= "<pre class='diff'>".$cl->{"diff"}."</pre>"
                if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"});
    # SHOT
            $this_day_result .= "<img src='"
                    .$Config{l3shot_path}
                    .$cl->{"screenshot"}
                    ."' alt ='screenshot id ".$cl->{"screenshot"}
                    ."'/>"
                if ( $Config{"show_screenshots"} =~ /^y/i && $cl->{"screenshot"});
    #NOTES
            if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
                my $note=$cl->{"note"};
                $note =~ s/\n/<br\/>\n/msg;
                if (not $note =~ s@(http:[a-zA-Z.0-9/_?%-]*)@<a href='$1'>$1</a>@g) {
                  $note =~ s@(www\.[a-zA-Z.0-9/_?%-]*)@<a href='$1'>$1</a>@g;
                };
                $this_day_result .= "<div class='note'>";
                $this_day_result .= "<div class='note_title'>".$cl->{note_title}."</div>" if $cl->{note_title};
                $this_day_result .= "<div class='note_text'>".$note."</div>";
                $this_day_result .= "</div>\n";
            }
            # Вывод очередной команды окончен
            $this_day_result .= "</div>\n";                     # cblock
            $this_day_result .= "</td></tr></table></DIV>\n"
                             .  "</div>\n";                     # command
        }
        last: {
            $prev_unix_time=$first_command_of_the_day_unix_time;
            $first_command_of_the_day_unix_time = $cl->{time};
            $human_readable_time = strftime "%D", localtime($prev_unix_time);
            $result .= "<h3 id='day_on_sec_$prev_unix_time'>".$Day_Name[$last_wday]." ($human_readable_time)</h3>";
            for my $entry_class (keys %new_entries_of) {
                my $table_caption = "Таблица ".$table_number++.".".$Day_Name[$last_wday]
                                  . ". Новые ".$new_entries_of{$entry_class};
                my $new_commands_section = make_new_entries_table(
                                            $table_caption,
                                            $entry_class=~/[0-9]+\s+(.*)/,
                                            \@known_commands);
            }
            @known_commands = keys %frequency_of_command;
            $result .= $this_day_result;
       }
        return ($result, collapse_list (\@toc));
    }
    #############
    # make_new_entries_table
    #
    # Напечатать таблицу неизвестных команд
    #
    # In:       $_[0]       table_caption
    #           $_[1]       entries_class
    #           @_[2..]     known_commands
    # Out:
    sub make_new_entries_table
    {
        my $table_caption;
        my $entries_class = shift;
        my @known_commands = @{$_[0]};
        my $result = "";
        my %count;
        my @new_commands = ();
        for my $c (keys %frequency_of_command, @known_commands) {
            $count{$c}++
        }
        for my $c (keys %frequency_of_command) {
            push @new_commands, $c if $count{$c} != 2;
        }
        my $new_commands_section;
        if (@new_commands){
            my $hint;
            for my $c (reverse sort { $frequency_of_command{$a} <=> $frequency_of_command{$b} } @new_commands) {
                    $hint = make_comment($c);
                    next unless $hint;
                    my ($command, $hint) = $hint =~ m/(.*?) \s*- \s*(.*)/;
                    next unless $command =~ s/\($entries_class\)//i;
                    $new_commands_section .= "<tr><td valign='top'>$command</td><td>$hint</td></tr>";
            }
        }
        if ($new_commands_section) {
            $result .= "<table class='new_commands_table' width='700' cellspacing='0' cellpadding='0'>"
                    .  "<tr class='new_commands_caption'>"
                    .  "<td colspan='2' align='right'>$table_caption</td>"
                    .  "</tr>"
                    .  "<tr class='new_commands_header'>"
                    .  "<td width=100>Команда</td><td width=600>Описание</td>"
                    .  "</tr>"
                    .  $new_commands_section
                    .  "</table>"
        }
        return $result;
    }
    #############
    # minutes_passed
    #
    #
    #
    # In:       $_[0]       seconds_since_last_command
    # Out:                  "minutes passed" text
    sub minutes_passed
    {
            my $seconds_since_last_command = shift;
            my $result = "";
            if ($seconds_since_last_command > 7200) {
                my $hours_passed =  int($seconds_since_last_command/3600);
                my $passed_word  = $hours_passed % 10 == 1 ? "прошла"
                                                             : "прошло";
                my $hours_word   = $hours_passed % 10 == 1 ?   "часа":
                                                               "часов";
                $result .= "<div class='much_time_passed'>"
                        .  $passed_word." &gt;".$hours_passed." ".$hours_word
                        .  "</div>\n";
            }
            elsif ($seconds_since_last_command > 600) {
                my $minutes_passed =  int($seconds_since_last_command/60);
                my $passed_word  = $minutes_passed % 100 > 10
                                && $minutes_passed % 100 < 20 ? "прошло"
                                 : $minutes_passed % 10 == 1  ? "прошла"
                                                              : "прошло";
                my $minutes_word = $minutes_passed % 100 > 10
                                && $minutes_passed % 100 < 20 ? "минут" :
                                   $minutes_passed % 10 == 1 ? "минута":
                                   $minutes_passed % 10 == 0 ? "минут" :
                                   $minutes_passed % 10  > 4 ? "минут" :
                                                               "минуты";
                if ($seconds_since_last_command < 1800) {
                    $result .= "<div class='time_passed'>"
                            .  $passed_word." ".$minutes_passed." ".$minutes_word
                            .  "</div>\n";
                }
                else {
                    $result .= "<div class='much_time_passed'>"
                            .  $passed_word." ".$minutes_passed." ".$minutes_word
                            .  "</div>\n";
                }
            }
            return $result;
    }
    #############
    # print_all_txt
    #
    # Вывести журнал в текстовом формате
    #
    # In:       $_[0]       output_filename
    # Out:
    sub print_command_lines_txt
    {
        my $output_filename=$_[0];
        my $note_number=0;
        my $result = q();
        my $this_day_resut = q();
        my $cl;
        my $last_tty="";
        my $last_session="";
        my $last_day=q();
        my $last_wday=q();
        my $in_range=0;
        my $current_command=0;
        my $cursor_position = 0;
        if ($Config{filter}) {
            # Инициализация фильтра
            for (split /&/,$Config{filter}) {
                my ($var, $val) = split /=/;
                $filter{$var} = $val || "";
            }
        }
    COMMAND_LINE:
        for my $k (@Command_Lines_Index) {
            my $cl=$Command_Lines[$Command_Lines_Index[$current_command++]];
            next unless $cl;
    # Пропускаем строки, которые противоречат фильтру
    # Если у нас недостаточно информации о том, подходит строка под  фильтр или нет,
    # мы её выводим
            for my $filter_key (keys %filter) {
                next COMMAND_LINE
                    if defined($cl->{local_session_id})
                    && defined($Sessions{$cl->{local_session_id}}->{$filter_key})
                    && $Sessions{$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
            }
    # Пропускаем строки, выходящие за границу "signature",
    # при условии, что границы указаны
    # Пропускаем неправильные/прерванные/другие команды
            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);
    #
    ##
    ## Начинается собственно вывод
    ##
    #
    ### Сначала обрабатываем границы разделов
    ### Если тип команды "note", это граница
            if ($cl->{class} eq "note") {
                $this_day_result .= " === ".$cl->{note_title}." === \n" if $cl->{note_title};
                $this_day_result .= $cl->{note}."\n";
                next;
            }
            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->{"class"};
    # DAY CHANGE
            if ( $last_day ne $day) {
                if ($last_day) {
                    $result .= "== ".$Day_Name[$last_wday]." == \n";
                    $result .= $this_day_result;
                }
                $last_day   = $day;
                $last_wday  = $wday;
                $this_day_result = q();
            }
    # CONSOLE CHANGE
            if ($cl->{"tty"} && $last_tty ne $cl->{"tty"} && 0) {
                my $tty = $cl->{"tty"};
                $this_day_result .= "         #l3: ------- другая консоль ----\n";
                $last_tty=$cl->{"tty"};
            }
    # Session change
            if ( $last_session ne $cl->{"local_session_id"}) {
                $this_day_result .= "# ------------------------------------------------------------"
                                 .  "  l3: local_session_id=".$cl->{"local_session_id"}
                                 .  " ---------------------------------- \n";
                $last_session=$cl->{"local_session_id"};
            }
    # TIME
            my @nl_counter = split (/\n/, $result);
            $cursor_position=length($result) - @nl_counter;
            if ($Config{"show_time"} =~ /^y/i) {
                $this_day_result .= "$hour:$min:$sec"
            }
    # COMMAND
            $this_day_result .= " ".$cl->{"prompt"}.$cl->{"cline"}."\n";
            if ($cl->{"err"}) {
                $this_day_result .= "         #l3: err=".$cl->{'err'}."\n";
            }
    # OUTPUT
            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"}})
                )) {
                my $output = $cl->{short_output};
                if ($output) {
                     $output =~ s/^/         |/mg;
                }
                $this_day_result .= $output;
            }
    # DIFF
            if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
                my $diff = $cl->{"diff"};
                $diff =~ s/^/         |/mg;
                $this_day_result .= $diff;
            };
    # SHOT
            if ($Config{"show_screenshots"} =~ /^y/i && $cl->{"screenshot"}) {
                $this_day_result .= "         #l3: screenshot=".$cl->{'screenshot'}."\n";
            }
    #NOTES
            if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
                my $note=$cl->{"note"};
                $note =~ s/\n/\n#^/msg;
                $this_day_result .= "#^ == ".$cl->{note_title}." ==\n" if $cl->{note_title};
                $this_day_result .= "#^ ".$note."\n";
            }
        }
        last: {
            $result .= "== ".$Day_Name[$last_wday]." == \n";
            $result .= $this_day_result;
       }
       return $result;
    }
    #############
    # print_edit_all_html
    #
    # Вывести страницу с текстовым представлением журнала для редактирования
    #
    # In:       $_[0]       output_filename
    # Out:
    sub print_edit_all_html
    {
        my $output_filename= shift;
        my $result;
        my $cursor_position = 0;
        $result = print_command_lines_txt;
        my $title = ">Журнал лабораторных работ. Правка";
        $result =
                   "<html>"
                    ."<head>"
                    ."<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />"
                    ."<link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>"
                    ."<title>$title</title>"
                    ."</head>"
                  ."<script>"
                  .$SetCursorPosition_JS
                  ."</script>"
                  ."<body onLoad='setCursorPosition(document.all.mytextarea, $cursor_position, $cursor_position+10)'>"
                  ."<h1>Журнал лабораторных работ. Правка</h1>"
                  ."<form>"
                  ."<textarea rows='30' cols='100' wrap='off' id='mytextarea'>$result</textarea>"
                  ."<br/><input type='submit' value='Сохранить' label='label'/>"
                  ."</form>"
                  ."<p>Внимательно правим, потом сохраняем</p>"
                  ."<p>Строки, начинающиеся символами #l3: можно трогать, только если точно знаешь, что делаешь</p>"
                  ."</body>"
                  ."</html>";
        if ($output_filename eq "-") {
            print $result;
        }
        else {
            open(OUT, ">", $output_filename)
                or die "Can't open $output_filename for writing\n";
            binmode ":utf8";
            print OUT "$result";
            close(OUT);
        }
    }
    #############
    # print_all_txt
    #
    # Вывести страницу с текстовым представлением журнала для редактирования
    #
    # In:       $_[0]       output_filename
    # Out:
    sub print_all_txt
    {
        my $result;
        $result = print_command_lines_txt;
        $result =~ s/&gt;/>/g;
        $result =~ s/&lt;/</g;
        $result =~ s/&amp;/&/g;
        if ($output_filename eq "-") {
            print $result;
        }
        else {
            open(OUT, ">:utf8", $output_filename)
                or die "Can't open $output_filename for writing\n";
            print OUT "$result";
            close(OUT);
        }
    }
    #############
    # print_all_html
    #
    #
    #
    # In:       $_[0]       output_filename
    # Out:
    sub print_all_html
    {
        my $output_filename=$_[0];
        my $result;
        my ($command_lines,$toc)  = print_command_lines_html;
        my $files_section         = print_files_html;
        $result = $debug_output;
        $result .= print_header_html($toc);
    #    $result.= join " <br/>", keys %Sessions;
    #    for my $sess (keys %Sessions) {
    #            $result .= join " ", keys (%{$Sessions{$sess}});
    #            $result .= "<br/>";
    #    }
        $result.= "<h2 id='log'>Журнал</h2>"       . $command_lines;
        $result.= "<h2 id='files'>Файлы</h2>"      . $files_section if $files_section;
        $result.= "<h2 id='stat'>Статистика</h2>"  . print_stat_html;
        $result.= "<h2 id='help'>Справка</h2>"     . $Html_Help . "<br/>";
        $result.= "<h2 id='about'>О программе</h2>". $Html_About. "<br/>";
        $result.= print_footer_html;
        if ($output_filename eq "-") {
            binmode STDOUT, ":utf8";
            print $result;
        }
        else {
            open(OUT, ">:utf8", $output_filename)
                or die "Can't open $output_filename for writing\n";
            print OUT $result;
            close(OUT);
        }
    }
    #############
    # print_header_html
    #
    #
    #
    # In:   $_[0]       Содержание
    # Out:              Распечатанный заголовок
    sub print_header_html
    {
        my $toc = $_[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"};
        my $title    = "Журнал лабораторных работ";
        $title      .= " -- ".$course_student if $course_student;
        if ($course_date) {
            $title  .= " -- ".$course_date;
            $title  .= $course_code ? "/".$course_code
                                    : "";
        }
        else {
            $title  .= " -- ".$course_code if $course_code;
        }
        # Управляющая форма
        my $control_form .= "<div class='visibility_form' title='Выберите какие элементы должны быть показаны в журнале'>"
                         .  "<span class='header'>Видимые элементы</span>"
                         .  "<span class='window_controls'><a href='' onclick='' title='свернуть форму управления'>_</a> <a href='' onclick='' title='закрыть форму управления'>x</a></span>"
                         .  "<div><form>\n";
        for my $element (sort keys %Elements_Visibility)
        {
            my ($skip, @e) = split /\s+/, $element;
            my $showhide = join "", map { "ShowHide('$_');" } @e ;
            $control_form .= "<div><input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
                    $Elements_Visibility{$element}.
                    "</input></div>";
        }
        $control_form .= "</form>\n"
                      .  "</div>\n";
        # Управляющая форма отключена
        # Она слишком сильно мешает, нужно что-то переделать
        $control_form = "";
        my $tigra_hints_array=tigra_hints_generate;
        my $result;
        $result = <<HEADER;
        <html>
        <head>
        <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
        <link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>
        <title>$title</title>
        </head>
        <body>
        <!--<script>
        $Html_JavaScript
        </script>-->
    <!-- vvv Tigra Hints vvv -->
    <script language="JavaScript" src="/tigra/hints.js"></script>
    <!--<script language="JavaScript" src="/tigra/hints_cfg.js"></script>-->
    <script>$tigra_hints_array</script>
    <style>
    /* a class for all Tigra Hints boxes, TD object */
        .hintsClass
            {text-align: left; font-size:80%; font-family: Verdana, Arial, Helvetica; background-color:#ffffee; padding: 0px 0px 0px 0px;}
    /* this class is used by Tigra Hints wrappers */
        .row
            {background: white;}
        .bl2 {border: 1px solid #e68200; background:url(/tigra/block/bl2.gif) 0 100% no-repeat; text-align:left}
        .bl {background:url(/tigra/block/bl2.gif) 0 100% no-repeat; text-align:left}
        .br {background:url(/tigra/block/br2.gif) 100% 100% no-repeat}
        .tl {background:url(/tigra/block/tl2.gif) 0 0 no-repeat}
        .tr {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat; padding:10px}
        .tr2 {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat}
        .t {background:url(/tigra/block/dot2.gif) 0 0 repeat-x}
        .b {background:url(/tigra/block/dot2.gif) 0 100% repeat-x}
        .l {background:url(/tigra/block/dot2.gif) 0 0 repeat-y}
        .r {background:url(/tigra/block/dot2.gif) 100% 0 repeat-y}
    </style>
    <!-- ^^^ Tigra Hints ^^^ -->
    <!--
        .bl2 {border: 1px solid #e68200; background:url(/tigra/block/bl2.gif) 0 100% no-repeat; width:20em; text-align:center}
        .bl {background:url(/tigra/block/bl2.gif) 0 100% no-repeat; width:20em; text-align:center}
        .br {background:url(/tigra/block/br2.gif) 100% 100% no-repeat}
        .tl {background:url(/tigra/block/tl2.gif) 0 0 no-repeat}
        .tr {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat; padding:10px}
        .tr2 {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat}
        .t {background:url(/tigra/block/dot2.gif) 0 0 repeat-x; width:20em}
        .b {background:url(/tigra/block/dot2.gif) 0 100% repeat-x}
        .l {background:url(/tigra/block/dot2.gif) 0 0 repeat-y}
        .r {background:url(/tigra/block/dot2.gif) 100% 0 repeat-y}
    -->
        <div class='edit_link'>
        [ <a href='?action=edit&$filter_url'>править</a> ]
        </div>
        <h1 onmouseover="myHint.show('1')" onmouseout="myHint.hide()" class='lined_header'>Журнал лабораторных работ</h1>
    HEADER
        if (    $course_student
                || $course_trainer
                || $course_name
                || $course_code
                || $course_date
                || $course_center) {
                $result .= "<p>";
                $result .= "Выполнил $course_student<br/>"  if $course_student;
                $result .= "Проверил $course_trainer <br/>" if $course_trainer;
                $result .= "Курс "                          if $course_name
                                                                || $course_code
                                                                || $course_date;
                $result .= "$course_name "                  if $course_name;
                $result .= "($course_code)"                 if $course_code;
                $result .= ", $course_date<br/>"            if $course_date;
                $result .= "Учебный центр $course_center <br/>" if $course_center;
                $result .= "Фильтр ".join(" ", map("$filter{$_}=$_", keys %filter))."<br/>" if %filter;
                $result .= "</p>";
        }
        $result .= <<HEADER;
        <table width='100%'>
        <tr>
        <td width='*'>
        <table border=0 id='toc' class='toc'>
        <tr>
        <td>
        <div class='toc_title'>Содержание</div>
        <ul>
            <li><a href='#log'>Журнал</a></li>
            <ul>$toc</ul>
            <li><a href='#files'>Файлы</a></li>
            <li><a href='#stat'>Статистика</a></li>
            <li><a href='#help'>Справка</a></li>
            <li><a href='#about'>О программе</a></li>
        </ul>
        </td>
        </tr>
        </table>
        </td>
        <td valign='top' width=200>$control_form</td>
        </tr>
        </table>
    HEADER
        return $result;
    }
    #############
    # print_footer_html
    #
    #
    #
    #
    #
    sub print_footer_html
    {
        return "</body>\n</html>\n";
    }
    #############
    # print_stat_html
    #
    #
    #
    # In:
    # Out:
    sub print_stat_html
    {
        %StatNames = (
            FirstCommand        => "Время первой команды журнала",
            LastCommand         => "Время последней команды журнала",
            TotalCommands       => "Количество командных строк в журнале",
            ErrorsPercentage    => "Процент команд с ненулевым кодом завершения, %",
            MistypesPercentage  => "Процент синтаксически неверно набранных команд, %",
            TotalTime           => "Суммарное время работы с терминалом <sup><font size='-2'>*</font></sup>, час",
            CommandsPerTime     => "Количество командных строк в единицу времени, команда/мин",
            CommandsFrequency   => "Частота использования команд",
            RareCommands        => "Частота использования этих команд < 0.5%",
        );
        @StatOrder = (
            FirstCommand,
            LastCommand,
            TotalCommands,
            ErrorsPercentage,
            MistypesPercentage,
            TotalTime,
            CommandsPerTime,
            CommandsFrequency,
            RareCommands,
        );
        # Подготовка статистики к выводу
        # Некоторые значения пересчитываются!
        # Дальше их лучше уже не использовать!!!
        my %CommandsFrequency = %frequency_of_command;
        $Stat{TotalTime} ||= 0;
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{FirstCommand} || 0);
        $Stat{FirstCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec,  $year+1900, $mon+1, $mday;
        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{LastCommand} || 0);
        $Stat{LastCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec,  $year+1900, $mon+1, $mday;
        if ($Stat{TotalCommands}) {
            $Stat{ErrorsPercentage} = sprintf "%5.2f", $Stat{ErrorCommands}*100/$Stat{TotalCommands};
            $Stat{MistypesPercentage} = sprintf "%5.2f", $Stat{MistypedCommands}*100/$Stat{TotalCommands};
        }
        $Stat{CommandsPerTime} = sprintf "%5.2f", $Stat{TotalCommands}*60/$Stat{TotalTime}
            if $Stat{TotalTime};
        $Stat{TotalTime} = sprintf "%5.2f", $Stat{TotalTime}/60/60;
        my $total_commands=0;
        for $command (keys %CommandsFrequency){
            $total_commands += $CommandsFrequency{$command};
        }
        if ($total_commands) {
            for $command (reverse sort {$CommandsFrequency{$a} <=> $CommandsFrequency{$b}} keys %CommandsFrequency){
                my $command_html;
                my $percentage = sprintf "%5.2f",$CommandsFrequency{$command}*100/$total_commands;
                if ($percentage < 0.5) {
                    my $hint = make_comment($command);
                    $command_html = "$command";
                    $command_html = "<span title='$hint' class='with_hint'>$command_html</span>" if $hint;
                    $command_html = "<span class='without_hint'>$command_html</span>" if not $hint;
                    my $command_html = "<tt>$command_html</tt>";
                    $Stat{RareCommands} .= $command_html."<sub><font size='-2'>".$CommandsFrequency{$command}."</font></sub> , ";
                }
                else {
                    my $hint = make_comment($command);
                    $command_html = "$command";
                    $command_html = "<span title='$hint' class='with_hint'>$command_html</span>" if $hint;
                    $command_html = "<span class='without_hint'>$command_html</span>" if not $hint;
                    my $command_html = "<tt>$command_html</tt>";
                    $percentage = sprintf "%5.2f",$percentage;
                    $Stat{CommandsFrequency} .= "<tr><td>".$command_html."</td><td>".$CommandsFrequency{$command}."</td>".
                        "<td>|".("="x int($CommandsFrequency{$command}*100/$total_commands))."| $percentage%</td></tr>";
                }
            }
            $Stat{CommandsFrequency} = "<table>".$Stat{CommandsFrequency}."</table>";
            $Stat{RareCommands} =~ s/, $// if $Stat{RareCommands};
        }
        my $result = q();
        for my $stat (@StatOrder) {
            next unless $Stat{"$stat"};
            $result .= "<tr valign='top'><td width='300'>".$StatNames{"$stat"}."</td><td>".$Stat{"$stat"}."</td></tr>"
        }
        $result  = "<table>$result</table>"
                 . "<font size='-2'>____<br/>*) Интервалы неактивности длительностью "
                 .  ($Config{stat_inactivity_interval}/60)
                 . " минут и более не учитываются</font></br>";
        return $result;
    }
    sub collapse_list($)
    {
        my $res = "";
        for my $elem (@{$_[0]}) {
            if (ref $elem eq "ARRAY") {
                $res .= "<ul>".collapse_list($elem)."</ul>";
            }
            else
            {
                $res .= "<li>".$elem."</li>";
            }
        }
        return $res;
    }
    sub print_files_html
    {
        my $result = qq();
        my @toc;
        for my $file (sort keys %Files) {
              my $div_id = "file:$file";
              $div_id =~ s@/@_@g;
              push @toc, "<a href='#$div_id'>$file</a>";
              $result .= "<div class='filename' id='$div_id'>".$file."</div>\n"
                      .  "<div class='file_navigation'><a href='#command:".$Files{$file}->{source_command_id}."'>"."&gt;"."</a></div>"
                      .  "<div class='filedata'><pre>".$Files{$file}->{content}."</pre></div>";
        }
        if ($result) {
            return "<div class='files_toc'>".collapse_list(\@toc)."</div>".$result;
        }
        else {
            return "";
        }
    }
    sub init_variables
    {
    $Html_Help = <<HELP;
        Для того чтобы использовать LiLaLo, не нужно знать ничего особенного:
        всё происходит само собой.
        Однако, чтобы ведение и последующее использование журналов
        было как можно более эффективным, желательно иметь в виду следующее:
        <ol>
        <li><p>
        В журнал автоматически попадают все команды, данные в любом терминале системы.
        </p></li>
        <li><p>
        Для того чтобы убедиться, что журнал на текущем терминале ведётся,
        и команды записываются, дайте команду w.
        В поле WHAT, соответствующем текущему терминалу,
        должна быть указана программа script.
        </p></li>
        <li><p>
        Команды, при наборе которых были допущены синтаксические ошибки,
        выводятся перечёркнутым текстом:
    <table>
    <tr class='command'>
    <td class='script'>
    <pre class='_mistyped_cline'>
    \$ l s-l</pre>
    <pre class='_mistyped_output'>bash: l: command not found
    </pre>
    </td>
    </tr>
    </table>
    <br/>
        </p></li>
        <li><p>
        Если код завершения команды равен нулю,
        команда была выполнена без ошибок.
        Команды, код завершения которых отличен от нуля, выделяются цветом.
    <table>
    <tr class='command'>
    <td class='script'>
    <pre class='_wrong_cline'>
    \$ test 5 -lt 4</pre>
    </pre>
    </td>
    </tr>
    </table>
        Обратите внимание на то, что код завершения команды может быть отличен от нуля
        не только в тех случаях, когда команда была выполнена с ошибкой.
        Многие команды используют код завершения, например, для того чтобы показать результаты проверки
    <br/>
        </p></li>
        <li><p>
        Команды, ход выполнения которых был прерван пользователем, выделяются цветом.
    <table>
    <tr class='command'>
    <td class='script'>
    <pre class='_interrupted_cline'>
    \$ find / -name abc</pre>
    <pre class='interrupted_output'>find: /home/devi-orig/.gnome2: Keine Berechtigung
    find: /home/devi-orig/.gnome2_private: Keine Berechtigung
    find: /home/devi-orig/.nautilus/metafiles: Keine Berechtigung
    find: /home/devi-orig/.metacity: Keine Berechtigung
    find: /home/devi-orig/.inkscape: Keine Berechtigung
    ^C
    </pre>
    </td>
    </tr>
    </table>
    <br/>
        </p></li>
        <li><p>
        Команды, выполненные с привилегиями суперпользователя,
        выделяются слева красной чертой.
    <table>
    <tr class='command'>
    <td class='script'>
    <pre class='_root_cline'>
    # id</pre>
    <pre class='_root_output'>
    uid=0(root) gid=0(root) Gruppen=0(root)
    </pre>
    </td>
    </tr>
    </table>
        <br/>
        </p></li>
        <li><p>
        Изменения, внесённые в текстовый файл с помощью редактора,
        запоминаются и показываются в журнале в формате ed.
        Строки, начинающиеся символом "&lt;", удалены, а строки,
        начинающиеся символом "&gt;" -- добавлены.
    <table>
    <tr class='command'>
    <td class='script'>
    <pre class='cline'>
    \$ vi ~/.bashrc</pre>
    <table><tr><td width='5'/><td class='diff'><pre>2a3,5
    &gt;    if [ -f /usr/local/etc/bash_completion ]; then
    &gt;         . /usr/local/etc/bash_completion
    &gt;        fi
    </pre></td></tr></table></td>
    </tr>
    </table>
        <br/>
        </p></li>
        <li><p>
        Для того чтобы изменить файл в соответствии с показанными в диффшоте
        изменениями, можно воспользоваться командой patch.
        Нужно скопировать изменения, запустить программу patch, указав в
        качестве её аргумента файл, к которому применяются изменения,
        и всавить скопированный текст:
    <table>
    <tr class='command'>
    <td class='script'>
    <pre class='cline'>
    \$ patch ~/.bashrc</pre>
    </td>
    </tr>
    </table>
        В данном случае изменения применяются к файлу ~/.bashrc
        </p></li>
        <li><p>
        Для того чтобы получить краткую справочную информацию о команде,
        нужно подвести к ней мышь. Во всплывающей подсказке появится краткое
        описание команды.
        </p>
        <p>
        Если справочная информация о команде есть,
        команда выделяется голубым фоном, например: <span class="with_hint" title="главный текстовый редактор Unix">vi</span>.
        Если справочная информация отсутствует,
        команда выделяется розовым фоном, например: <span class="without_hint">notepad.exe</span>.
        Справочная информация может отсутствовать в том случае,
        если (1) команда введена неверно; (2) если распознавание команды LiLaLo выполнено неверно;
        (3) если информация о команде неизвестна LiLaLo.
        Последнее возможно для редких команд.
        </p></li>
        <li><p>
        Большие, в особенности многострочные, всплывающие подсказки лучше
        всего показываются браузерами KDE Konqueror, Apple Safari и Microsoft Internet Explorer.
        В браузерах Mozilla и Firefox они отображаются не полностью,
        а вместо перевода строки выводится специальный символ.
        </p></li>
        <li><p>
        Время ввода команды, показанное в журнале, соответствует времени
        <i>начала ввода командной строки</i>, которое равно тому моменту,
        когда на терминале появилось приглашение интерпретатора
        </p></li>
        <li><p>
        Имя терминала, на котором была введена команда, показано в специальном блоке.
        Этот блок показывается только в том случае, если терминал
        текущей команды отличается от терминала предыдущей.
        </p></li>
        <li><p>
        Вывод не интересующих вас в настоящий момент элементов журнала,
        таких как время, имя терминала и других, можно отключить.
        Для этого нужно воспользоваться <a href='#visibility_form'>формой управления журналом</a>
        вверху страницы.
        </p></li>
        <li><p>
        Небольшие комментарии к командам можно вставлять прямо из командной строки.
        Комментарий вводится прямо в командную строку, после символов #^ или #v.
        Символы ^ и v показывают направление выбора команды, к которой относится комментарий:
        ^ - к предыдущей, v - к следующей.
        Например, если в командной строке было введено:
    <pre class='cline'>
    \$ whoami
    </pre>
    <pre class='output'>
    user
    </pre>
    <pre class='cline'>
    \$ #^ Интересно, кто я?
    </pre>
        в журнале это будет выглядеть так:
    <pre class='cline'>
    \$ whoami
    </pre>
    <pre class='output'>
    user
    </pre>
    <table class='note'><tr><td width='100%' class='note_text'>
    <tr> <td> Интересно, кто я?<br/> </td></tr></table>
        </p></li>
        <li><p>
        Если комментарий содержит несколько строк,
        его можно вставить в журнал следующим образом:
    <pre class='cline'>
    \$ whoami
    </pre>
    <pre class='output'>
    user
    </pre>
    <pre class='cline'>
    \$ cat > /dev/null #^ Интересно, кто я?
    </pre>
    <pre class='output'>
    Программа whoami выводит имя пользователя, под которым
    мы зарегистрировались в системе.
    -
    Она не может ответить на вопрос о нашем назначении
    в этом мире.
    </pre>
        В журнале это будет выглядеть так:
    <table>
    <tr class='command'>
    <td class='script'>
    <pre class='cline'>
    \$ whoami</pre>
    <pre class='output'>user
    </pre>
    <table class='note'><tr><td class='note_title'>Интересно, кто я?</td></tr><tr><td width='100%' class='note_text'>
    Программа whoami выводит имя пользователя, под которым<br/>
    мы зарегистрировались в системе.<br/>
    <br/>
    Она не может ответить на вопрос о нашем назначении<br/>
    в этом мире.<br/>
    </td></tr></table>
    </td>
    </tr>
    </table>
        Для разделения нескольких абзацев между собой
        используйте символ "-", один в строке.
        <br/>
    </p></li>
        <li><p>
        Комментарии, не относящиеся непосредственно ни к какой из команд,
        добавляются точно таким же способом, только вместо симолов #^ или #v
        нужно использовать символы #=
        </p></li>
        <p><li>
        Содержимое файла может быть показано в журнале.
        Для этого его нужно вывести с помощью программы cat.
        Если вывод команды отметить симоволами #!,
        содержимое файла будет показано в журнале
        в специально отведённой для этого секции.
        </li></p>
        <p>
        <li>
        Для того чтобы вставить скриншот интересующего вас окна в журнал,
        нужно воспользоваться командой l3shot.
        После того как команда вызвана, нужно с помощью мыши выбрать окно, которое
        должно быть в журнале.
        </li>
        </p>
        <p>
        <li>
        Команды в журнале расположены в хронологическом порядке.
        Если две команды давались одна за другой, но на разных терминалах,
        в журнале они будут рядом, даже если они не имеют друг к другу никакого отношения.
    <pre>
    1
        2
    3
        4
    </pre>
        Группы команд, выполненных на разных терминалах, разделяются специальной линией.
        Под этой линией в правом углу показано имя терминала, на котором выполнялись команды.
        Для того чтобы посмотреть команды только одного сенса,
        нужно щёкнуть по этому названию.
        </li>
        </p>
    </ol>
    HELP
    $Html_About = <<ABOUT;
        <p>
        <a href='http://xgu.ru/lilalo/'>LiLaLo</a> (L3) расшифровывается как Live Lab Log.<br/>
        Программа разработана для повышения эффективности обучения Unix/Linux-системам.<br/>
        (c) Игорь Чубин, 2004-2008<br/>
        </p>
    ABOUT
    $Html_About.='$Id: l3-frontend,v 1.45 2008-03-13 10:19:42 igor Exp $ </p>';
    $Html_JavaScript = <<JS;
        function getElementsByClassName(Class_Name)
        {
            var Result=new Array();
            var All_Elements=document.all || document.getElementsByTagName('*');
            for (i=0; i<All_Elements.length; i++)
                if (All_Elements[i].className==Class_Name)
            Result.push(All_Elements[i]);
            return Result;
        }
        function ShowHide (name)
        {
            elements=getElementsByClassName(name);
            for(i=0; i<elements.length; i++)
                if (elements[i].style.display == "none")
                    elements[i].style.display = "";
                else
                    elements[i].style.display = "none";
                //if (elements[i].style.visibility == "hidden")
                //  elements[i].style.visibility = "visible";
                //else
                //  elements[i].style.visibility = "hidden";
        }
        function filter_by_output(text)
        {
            var jjj=0;
            elements=getElementsByClassName('command');
            for(i=0; i<elements.length; i++) {
                subelems = elements[i].getElementsByTagName('pre');
                for(j=0; j<subelems.length; j++) {
                    if (subelems[j].className = 'output') {
                        var str = new String(subelems[j].nodeValue);
                        if (jjj != 1) {
                            alert(str);
                            jjj=1;
                        }
                        if (str.indexOf(text) >0)
                            subelems[j].style.display = "none";
                        else
                            subelems[j].style.display = "";
                    }
                }
            }
        }
    JS
    $SetCursorPosition_JS = <<JS;
    function setCursorPosition(oInput,oStart,oEnd) {
        oInput.focus();
        if( oInput.setSelectionRange ) {
            oInput.setSelectionRange(oStart,oEnd);
        } else if( oInput.createTextRange ) {
            var range = oInput.createTextRange();
            range.collapse(true);
            range.moveEnd('character',oEnd);
            range.moveStart('character',oStart);
            range.select();
        }
    }
    JS
    %Search_Machines = (
            "google" =>     {   "query" =>  "http://www.google.com/search?q=" ,
                        "icon"  =>  "$Config{frontend_google_ico}" },
            "freebsd" =>    {   "query" =>  "http://www.freebsd.org/cgi/man.cgi?query=",
                        "icon"  =>  "$Config{frontend_freebsd_ico}" },
            "linux"  =>     {   "query" =>  "http://man.he.net/?topic=",
                        "icon"  =>  "$Config{frontend_linux_ico}"},
            "opennet"  =>   {   "query" =>  "http://www.opennet.ru/search.shtml?words=",
                        "icon"  =>  "$Config{frontend_opennet_ico}"},
            "local" =>  {   "query" =>  "http://www.freebsd.org/cgi/man.cgi?query=",
                        "icon"  =>  "$Config{frontend_local_ico}" },
        );
    %Elements_Visibility = (
            "0 new_commands_table"      =>  "новые команды",
            "1 diff"      =>  "редактор",
            "2 time"      =>  "время",
            "3 ttychange"     =>  "терминал",
            "4 wrong_output wrong_cline wrong_root_output wrong_root_cline"
                    =>  "команды с ненулевым кодом завершения",
            "5 mistyped_output mistyped_cline mistyped_root_output mistyped_root_cline"
                    =>  "неверно набранные команды",
            "6 interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline"
                    =>  "прерванные команды",
            "7 tab_completion_output tab_completion_cline"
                    =>  "продолжение с помощью tab"
    );
    @Day_Name      = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /;
    @Month_Name    = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /;
    @Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /;
    }
    # Временно удалённый код
    # Возможно, он не понадобится уже никогда
    sub search_by
    {
        my $sm = shift;
        my $topic = shift;
        $topic =~ s/ /+/;
        return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='".
                    $Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
    }
    ########################################################################################
    #
    # mywi
    #
    #
    #
    #
    #
    #
    #
    sub mywi_init
    {
        our $MyWiFile = "/home/igor/mywi/mywi.txt";
        our $MyWiLog = "/home/igor/mywi/mywi.log";
        our $section="";
        our @MywiTXT;       # Массив текстовых записей mywi
        our %MywiHASH;      # Хэш массивов записей
        our %Query;
        load_mywitxt($MyWiFile, \@MywiTXT, \%MywiHASH);
    }
    sub mywi_process_query($)
    #
    # Сделать подсказку по заданному запросу
    # $_[0] - тема для подсказки
    #
    # Возвращает:
    #   строку-подсказку
    #
    {
        my $query = shift;
        parse_query($query, \%Query);
        $result = search_in_txt(\%Query, \@MywiTXT, \%MywiHASH);
        if (!$result) {
            #add_to_log(\%Query, $MyWiLog);
            return "$query nothing appropriate.  Logged. ".join (";",%Query);
        }
        return $result;
    }
    ####################################################################################
    #                                   private section
    ####################################################################################
    sub load_mywitxt
    #
    # Загрузить файл с записями Mywi_TXT
    # в массив
    # $_[0] - указатель на массив для загрузки
    # $_[1] - имя файла для загрузки
    #
    {
        my $MyWiFile = $_[0];
        my $MywiTXT = $_[1];
        my $MywiHASH = $_[2];
        open (MW, "$MyWiFile") or die "Can't open $MyWiFile for reading";
        binmode MW, ":utf8";
        @{$MywiTXT} = <MW>;
        close (MWF);
        for my $mywi_line (@{$MywiTXT}) {
            my $topic = $mywi_line;
            $topic =~ s@\s*\(.*\n@@;
            push @{$$MywiHASH{"$topic"}}, $mywi_line;
    #        $MywiHASH{"$topic"} .= $mywi_line;
        }
    }
    sub parse_query
    #
    # Строка запроса:
    #   [format:]topic[(section)]
    # Элементы format и topic являются не обязательными
    #
    # $_[0] - строка запроса
    # $_[1] - ссылка на хэш запроса
    #
    {
        my $query_string = shift;
        my $query_hash = shift;
        %{$query_hash} = (
            "format"    =>  "txt",
            "section"   =>  "",
            "topic" =>  "",
        );
        if ($query_string =~ s/^([^:]*)://) {
            $query_hash->{"format"} = $1 || "txt";
        }
        if ($query_string =~ s/\(([^(]*)\)$//) {
            $query_hash->{"section"} = $1 || "";
        }
        $query_hash->{"topic"} = $query_string;
    }
    sub search_in_txt
    #
    # Выполнить поиск в текстовой базе
    # по известному запросу
    # $_[0] -- ссылка на хэш запроса
    # $_[1] -- ссылка на массив текстовых записей
    # $_[2] -- ссылка на хэш массивов текстовых записей
    # Результат:
    #   найденная текстовая запись в заданном формате
    #
    {
        my %Query = %{$_[0]};
        my %MywiHASH = %{$_[2]};
        my $topic = $Query{"topic"};
        my $section = $Query{"section"};
        my $result = "";
        return join("\n",@{$MywiHASH{"$topic"}})."\n";
        for my $l (@{$$_[2]{$topic}}) {
    #    for my $l (@{$_[1]}) {
            my $line = $l;
            if (
                ($section and $line =~ /^\s*\Q$topic\E\s*\($section*\)\s*-/ )
                or (not $section and $line =~ /^\s*\Q$topic\E\s*(\([^)]*\)?)\s*-/) ) {
                $line =~ s/^.* -//mg if ($Config{"short"});
                $result .= "<para>$line</para>";
            }
        }
        return $result;
    }
    sub add_to_log($$)
    #
    # Если в базе отсутствует информация по данной теме,
    # сделать предположение доступным способом
    # и добавить его в базу
    # или просто сделать отметку о необходимости
    # расширения базы
    #
    # Добавить запись в журнал
    # $_[0] - запись (ссылка на хэш)
    # $_[1] - имя файла-журнала
    #
    {
        my $query = $_[0];
        my $MyWiLog = $_[1];
        open (MWF, ">>:utf8", $MyWiLog) or die "Can't open $MyWiLog for writing";
        my $my_guess = mywi_guess($query);
        print MWF "$my_guess\n";
        close(MWF);
    }
    sub mywi_guess($)
    # Сформировать исходную строку для журнала по заданному запросу
    # Если секция принадлежит 0..9, в качестве основы для результирующего текста использовать whatis
    # $_[0] - запись (ссылка на хэш)
    #
    # Возвращает:
    #   строку-предположение
    {
        my %query = %{$_[0]};
        my $topic = $query{"topic"};
        my $section = $query{"section"};
        my $result = "$topic($section)";
        if (!$section or $section =~ /^[1-9]$/)
        {
            # Запрос из категории 1-9
            # Об этом может знать whatis
            $result = `LANG=C whatis -- "$topic"`;
            if ($result =~ /nothing appropriate/i) {
                $result = $topic;
                $result .= "($section)" if $section;
            }
            else {
                1 while ($result =~ s/(\s+)-(\s+)/$1+$2/sg);
                $result =~ s/\s+\(/(/;
                chomp $result;
            }
        }
        return $result;
    }
    

    Статистика

    Время первой команды журнала09:18:31 2006- 9-12
    Время последней команды журнала07:34:01 2011- 9-12
    Количество командных строк в журнале101
    Процент команд с ненулевым кодом завершения, % 7.92
    Процент синтаксически неверно набранных команд, % 0.99
    Суммарное время работы с терминалом *, час 0.65
    Количество командных строк в единицу времени, команда/мин 2.60
    Частота использования команд
    cat18|===============| 15.65%
    l17|==============| 14.78%
    cd17|==============| 14.78%
    sudo6|=====| 5.22%
    vim6|=====| 5.22%
    grep6|=====| 5.22%
    l35|====| 4.35%
    w5|====| 4.35%
    ps4|===| 3.48%
    killall4|===| 3.48%
    l3-agent3|==| 2.61%
    l3-config3|==| 2.61%
    which3|==| 2.61%
    su3|==| 2.61%
    iptables2|=| 1.74%
    wget1|| 0.87%
    chmod1|| 0.87%
    cp1|| 0.87%
    28011|| 0.87%
    whic1|| 0.87%
    l3-1|| 0.87%
    0c1|| 0.87%
    tail1|| 0.87%
    11|| 0.87%
    l3-frontend1|| 0.87%
    exit1|| 0.87%
    0c11|| 0.87%
    tar1|| 0.87%
    ____
    *) Интервалы неактивности длительностью 30 минут и более не учитываются

    Справка

    Для того чтобы использовать LiLaLo, не нужно знать ничего особенного: всё происходит само собой. Однако, чтобы ведение и последующее использование журналов было как можно более эффективным, желательно иметь в виду следующее:
    1. В журнал автоматически попадают все команды, данные в любом терминале системы.

    2. Для того чтобы убедиться, что журнал на текущем терминале ведётся, и команды записываются, дайте команду w. В поле WHAT, соответствующем текущему терминалу, должна быть указана программа script.

    3. Команды, при наборе которых были допущены синтаксические ошибки, выводятся перечёркнутым текстом:
      $ l s-l
      bash: l: command not found
      

    4. Если код завершения команды равен нулю, команда была выполнена без ошибок. Команды, код завершения которых отличен от нуля, выделяются цветом.
      $ test 5 -lt 4
      Обратите внимание на то, что код завершения команды может быть отличен от нуля не только в тех случаях, когда команда была выполнена с ошибкой. Многие команды используют код завершения, например, для того чтобы показать результаты проверки

    5. Команды, ход выполнения которых был прерван пользователем, выделяются цветом.
      $ find / -name abc
      find: /home/devi-orig/.gnome2: Keine Berechtigung
      find: /home/devi-orig/.gnome2_private: Keine Berechtigung
      find: /home/devi-orig/.nautilus/metafiles: Keine Berechtigung
      find: /home/devi-orig/.metacity: Keine Berechtigung
      find: /home/devi-orig/.inkscape: Keine Berechtigung
      ^C
      

    6. Команды, выполненные с привилегиями суперпользователя, выделяются слева красной чертой.
      # id
      uid=0(root) gid=0(root) Gruppen=0(root)
      

    7. Изменения, внесённые в текстовый файл с помощью редактора, запоминаются и показываются в журнале в формате ed. Строки, начинающиеся символом "<", удалены, а строки, начинающиеся символом ">" -- добавлены.
      $ vi ~/.bashrc
      2a3,5
      >    if [ -f /usr/local/etc/bash_completion ]; then
      >         . /usr/local/etc/bash_completion
      >        fi
      

    8. Для того чтобы изменить файл в соответствии с показанными в диффшоте изменениями, можно воспользоваться командой patch. Нужно скопировать изменения, запустить программу patch, указав в качестве её аргумента файл, к которому применяются изменения, и всавить скопированный текст:
      $ patch ~/.bashrc
      В данном случае изменения применяются к файлу ~/.bashrc

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

      Если справочная информация о команде есть, команда выделяется голубым фоном, например: vi. Если справочная информация отсутствует, команда выделяется розовым фоном, например: notepad.exe. Справочная информация может отсутствовать в том случае, если (1) команда введена неверно; (2) если распознавание команды LiLaLo выполнено неверно; (3) если информация о команде неизвестна LiLaLo. Последнее возможно для редких команд.

    10. Большие, в особенности многострочные, всплывающие подсказки лучше всего показываются браузерами KDE Konqueror, Apple Safari и Microsoft Internet Explorer. В браузерах Mozilla и Firefox они отображаются не полностью, а вместо перевода строки выводится специальный символ.

    11. Время ввода команды, показанное в журнале, соответствует времени начала ввода командной строки, которое равно тому моменту, когда на терминале появилось приглашение интерпретатора

    12. Имя терминала, на котором была введена команда, показано в специальном блоке. Этот блок показывается только в том случае, если терминал текущей команды отличается от терминала предыдущей.

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

    14. Небольшие комментарии к командам можно вставлять прямо из командной строки. Комментарий вводится прямо в командную строку, после символов #^ или #v. Символы ^ и v показывают направление выбора команды, к которой относится комментарий: ^ - к предыдущей, v - к следующей. Например, если в командной строке было введено:

      $ whoami
      
      user
      
      $ #^ Интересно, кто я?
      
      в журнале это будет выглядеть так:
      $ whoami
      
      user
      
      Интересно, кто я?

    15. Если комментарий содержит несколько строк, его можно вставить в журнал следующим образом:

      $ whoami
      
      user
      
      $ cat > /dev/null #^ Интересно, кто я?
      
      Программа whoami выводит имя пользователя, под которым 
      мы зарегистрировались в системе.
      -
      Она не может ответить на вопрос о нашем назначении 
      в этом мире.
      
      В журнале это будет выглядеть так:
      $ whoami
      user
      
      Интересно, кто я?
      Программа whoami выводит имя пользователя, под которым
      мы зарегистрировались в системе.

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

    16. Комментарии, не относящиеся непосредственно ни к какой из команд, добавляются точно таким же способом, только вместо симолов #^ или #v нужно использовать символы #=

    17. Содержимое файла может быть показано в журнале. Для этого его нужно вывести с помощью программы cat. Если вывод команды отметить симоволами #!, содержимое файла будет показано в журнале в специально отведённой для этого секции.
    18. Для того чтобы вставить скриншот интересующего вас окна в журнал, нужно воспользоваться командой l3shot. После того как команда вызвана, нужно с помощью мыши выбрать окно, которое должно быть в журнале.
    19. Команды в журнале расположены в хронологическом порядке. Если две команды давались одна за другой, но на разных терминалах, в журнале они будут рядом, даже если они не имеют друг к другу никакого отношения.
      1
          2
      3   
          4
      
      Группы команд, выполненных на разных терминалах, разделяются специальной линией. Под этой линией в правом углу показано имя терминала, на котором выполнялись команды. Для того чтобы посмотреть команды только одного сенса, нужно щёкнуть по этому названию.

    О программе

    LiLaLo (L3) расшифровывается как Live Lab Log.
    Программа разработана для повышения эффективности обучения Unix/Linux-системам.
    (c) Игорь Чубин, 2004-2008

    $Id$