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

Содержание

Журнал

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

/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
07:34:02
#ls
AccessSpam.Reload                Fired_Users              Spamheaders
ArchCGP_Logs                     Getstat.pl               Sync_SA_DCC_Whitelist
Auto-Sa-learn                    ImInspector              VoipBalance
AutoCreateWhite_List             KillS99local             aimSniff.Cyr-005.pl
CGP                              MS_CGP_SYNC.sh           backup.ais
CGP_logs                         MailBoxFull              backup.ais.16
CSF                              MailFromSiteMrgarant.ru  backup.configs
Calls                            MakeSpam                 backup.nas
CheckRaids                       MaxSize                  backup.vm
Check_Samsdaemon                 Mount_ORION_Base         backup.wavs
...
Create_SPAM_Folder.MRGARANT2.pl  ReloadSquid              fincheck
Create_SPAM_Folder.MRGARANT3.pl  RemakeMailbox            get_temp_apc
Create_SPAM_Folder.pl            RemakeMailboxSPAM        mysqltuner.pl
DCC                              Restrict_SMTP            nOname1
DCC_stat                         Rsync_Zmail              restrictsmtp
DaySpamers                       SPAMERS                  rota
DayUP                            Sa-learn                 runflow
DecodeMp3toWav                   ShutdownUrodov           sPam
Del_CGP_dat_files                Sipnet_cash              xcode
Find_CGP_dat_files               Spam_to_mrgarant         xcodeln.pl
/dev/pts/1
07:34:02
#ls
AccessSpam.Reload                Fired_Users              Spamheaders
ArchCGP_Logs                     Getstat.pl               Sync_SA_DCC_Whitelist
Auto-Sa-learn                    ImInspector              VoipBalance
AutoCreateWhite_List             KillS99local             aimSniff.Cyr-005.pl
CGP                              MS_CGP_SYNC.sh           backup.ais
CGP_logs                         MailBoxFull              backup.ais.16
CSF                              MailFromSiteMrgarant.ru  backup.configs
Calls                            MakeSpam                 backup.nas
CheckRaids                       MaxSize                  backup.vm
Check_Samsdaemon                 Mount_ORION_Base         backup.wavs
...
Create_SPAM_Folder.MRGARANT2.pl  ReloadSquid              fincheck
Create_SPAM_Folder.MRGARANT3.pl  RemakeMailbox            get_temp_apc
Create_SPAM_Folder.pl            RemakeMailboxSPAM        mysqltuner.pl
DCC                              Restrict_SMTP            nOname1
DCC_stat                         Rsync_Zmail              restrictsmtp
DaySpamers                       SPAMERS                  rota
DayUP                            Sa-learn                 runflow
DecodeMp3toWav                   ShutdownUrodov           sPam
Del_CGP_dat_files                Sipnet_cash              xcode
Find_CGP_dat_files               Spam_to_mrgarant         xcodeln.pl
07:34:05
#Find_CGP_dat_files
umount: /mnt/dat: not mounted
mount.cifs kernel mount options: unc=//10.10.101.1\e$,user=root,,,,,domain=MRG,ver=1,credentials=/root/mount_smb_cred,rw,file_mode=0664,dir_mode=0775,iocharset=utf8,ip=10.10.101.1,pass=********
47312   /mnt/dat/PROFILES/aki_ey/Application Data/Microsoft/Outlook/Outlook/akimova@mrggroup.ru.dat
13156   /mnt/dat/PROFILES/bag_mb/Application Data/Microsoft/Outlook/Outlook/operator.dms@mrggroup.ru.dat
52968   /mnt/dat/PROFILES/ber_ig/Application Data/Microsoft/Outlook/Outlook/beresneva@mrggroup.ru.dat
16548   /mnt/dat/PROFILES/fed_om/Application Data/Microsoft/Outlook/Outlook/operator.dms@mrggroup.ru.dat
19872   /mnt/dat/PROFILES/fom_or/Application Data/Microsoft/Outlook/Outlook/fomina@mrggroup.ru.dat
1184404 /mnt/dat/PROFILES/gal_ed/Application Data/Microsoft/Outlook/Outlook/galicina@mrggroup.ru.dat
561128  /mnt/dat/PROFILES/ged_sv/Application Data/Microsoft/Outlook/Outlook/gede@mrggroup.ru.dat
46460   /mnt/dat/PROFILES/gor_ea/Application Data/Microsoft/Outlook/Outlook/e.a.gordeeva@mrggroup.ru.dat
...
972796  /mnt/dat/profiles$/pop_ev/Application Data/Microsoft/Outlook/Outlook/epopova@mrggroup.ru.dat
1335736 /mnt/dat/profiles$/sap_vn/Application Data/Microsoft/Outlook/Outlook/sapsai@mrggroup.ru.dat
400444  /mnt/dat/profiles$/sev_aa/Application Data/Microsoft/Outlook/Outlook/a.a.sevostianov@mrggroup.ru.dat
601140  /mnt/dat/profiles$/shk_ss.V2/AppData/Roaming/Microsoft/Outlook/Outlook/shkvarin@mrggroup.ru.dat
1383060 /mnt/dat/profiles$/svi_td/Application Data/Microsoft/Outlook/Outlook/t.d.sviridova@mrggroup.ru.dat
610748  /mnt/dat/profiles$/tep_la.V2/AppData/Roaming/Microsoft/Outlook/Outlook/teptsova@mrggroup.ru.dat
410844  /mnt/dat/profiles$/tru_ma/Application Data/Microsoft/Outlook/Outlook/ybitki@mrgarant.ru.dat
544272  /mnt/dat/profiles$/vor_ya/Application Data/Microsoft/Outlook/Outlook/y.a.vorontsova@mrggroup.ru.dat
1113804 /mnt/dat/profiles$/zlo_ov/Application Data/Microsoft/Outlook/Outlook/zlotya@mrggroup.ru.dat
21721472
/dev/pts/0
07:34:05
#Find_CGP_dat_files
umount: /mnt/dat: not mounted
mount.cifs kernel mount options: unc=//10.10.101.1\e$,user=root,,,,,domain=MRG,ver=1,credentials=/root/mount_smb_cred,rw,file_mode=0664,dir_mode=0775,iocharset=utf8,ip=10.10.101.1,pass=********
47312   /mnt/dat/PROFILES/aki_ey/Application Data/Microsoft/Outlook/Outlook/akimova@mrggroup.ru.dat
13156   /mnt/dat/PROFILES/bag_mb/Application Data/Microsoft/Outlook/Outlook/operator.dms@mrggroup.ru.dat
52968   /mnt/dat/PROFILES/ber_ig/Application Data/Microsoft/Outlook/Outlook/beresneva@mrggroup.ru.dat
16548   /mnt/dat/PROFILES/fed_om/Application Data/Microsoft/Outlook/Outlook/operator.dms@mrggroup.ru.dat
19872   /mnt/dat/PROFILES/fom_or/Application Data/Microsoft/Outlook/Outlook/fomina@mrggroup.ru.dat
1184404 /mnt/dat/PROFILES/gal_ed/Application Data/Microsoft/Outlook/Outlook/galicina@mrggroup.ru.dat
561128  /mnt/dat/PROFILES/ged_sv/Application Data/Microsoft/Outlook/Outlook/gede@mrggroup.ru.dat
46460   /mnt/dat/PROFILES/gor_ea/Application Data/Microsoft/Outlook/Outlook/e.a.gordeeva@mrggroup.ru.dat
...
972796  /mnt/dat/profiles$/pop_ev/Application Data/Microsoft/Outlook/Outlook/epopova@mrggroup.ru.dat
1335736 /mnt/dat/profiles$/sap_vn/Application Data/Microsoft/Outlook/Outlook/sapsai@mrggroup.ru.dat
400444  /mnt/dat/profiles$/sev_aa/Application Data/Microsoft/Outlook/Outlook/a.a.sevostianov@mrggroup.ru.dat
601140  /mnt/dat/profiles$/shk_ss.V2/AppData/Roaming/Microsoft/Outlook/Outlook/shkvarin@mrggroup.ru.dat
1383060 /mnt/dat/profiles$/svi_td/Application Data/Microsoft/Outlook/Outlook/t.d.sviridova@mrggroup.ru.dat
610748  /mnt/dat/profiles$/tep_la.V2/AppData/Roaming/Microsoft/Outlook/Outlook/teptsova@mrggroup.ru.dat
410844  /mnt/dat/profiles$/tru_ma/Application Data/Microsoft/Outlook/Outlook/ybitki@mrgarant.ru.dat
544272  /mnt/dat/profiles$/vor_ya/Application Data/Microsoft/Outlook/Outlook/y.a.vorontsova@mrggroup.ru.dat
1113804 /mnt/dat/profiles$/zlo_ov/Application Data/Microsoft/Outlook/Outlook/zlotya@mrggroup.ru.dat
21721472
/dev/pts/1
07:41:20
#ps fax
  PID TTY      STAT   TIME COMMAND
    2 ?        S<     0:00 [kthreadd]
    3 ?        S<     0:00  \_ [migration/0]
    4 ?        S<     0:05  \_ [ksoftirqd/0]
    5 ?        S<     0:00  \_ [watchdog/0]
    6 ?        S<     0:00  \_ [migration/1]
    7 ?        S<     0:20  \_ [ksoftirqd/1]
    8 ?        S<     0:00  \_ [watchdog/1]
    9 ?        S<     0:00  \_ [migration/2]
   10 ?        S<     0:18  \_ [ksoftirqd/2]
...
31098 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31099 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31100 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31101 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31102 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31103 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31104 ?        SN     0:00      \_ (unlinkd)
31229 ?        SN     0:01 /usr/local/bin/samsdaemon
17286 ?        SNl    0:43 /opt/openfire/jre/bin/java -server -DopenfireHome=/opt/openf
17350 ?        SNs    2:20 /sbin/syslogd -u syslogd -j /var/resolv
/dev/pts/0
07:41:20
#ps fax
  PID TTY      STAT   TIME COMMAND
    2 ?        S<     0:00 [kthreadd]
    3 ?        S<     0:00  \_ [migration/0]
    4 ?        S<     0:05  \_ [ksoftirqd/0]
    5 ?        S<     0:00  \_ [watchdog/0]
    6 ?        S<     0:00  \_ [migration/1]
    7 ?        S<     0:20  \_ [ksoftirqd/1]
    8 ?        S<     0:00  \_ [watchdog/1]
    9 ?        S<     0:00  \_ [migration/2]
   10 ?        S<     0:18  \_ [ksoftirqd/2]
...
31098 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31099 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31100 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31101 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31102 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31103 ?        SN     0:00      \_ (ntlm_auth) --helper-protocol=squid-2.5-ntlmssp
31104 ?        SN     0:00      \_ (unlinkd)
31229 ?        SN     0:01 /usr/local/bin/samsdaemon
17286 ?        SNl    0:43 /opt/openfire/jre/bin/java -server -DopenfireHome=/opt/openf
17350 ?        SNs    2:20 /sbin/syslogd -u syslogd -j /var/resolv
прошло 14 минут
/dev/pts/1
07:56:19
#ps fax | grep l3a
11348 pts/2    S+     0:00                                      \_ grep l3a
/dev/pts/0
07:56:19
#ps fax | grep l3a
11348 pts/2    S+     0:00                                      \_ grep l3a
/dev/pts/1
07:56:22
#l3
l3                 l3_close_session   l3mass_upload      l3upload
l3-agent           l3_fix_prompt      l3pwd
l3-backend         l3_save_last_line  l3script
l3-config          l3cd               l3shot
07:56:22
#ps fax | grep l3-a
11359 pts/2    S+     0:00                                      \_ grep l3-a
12491 ?        Ss     7:39 l3-agent
/dev/pts/0
07:56:22
#l3
l3                 l3_close_session   l3mass_upload      l3upload
l3-agent           l3_fix_prompt      l3pwd
l3-backend         l3_save_last_line  l3script
l3-config          l3cd               l3shot
07:56:22
#ps fax | grep l3-a
11359 pts/2    S+     0:00                                      \_ grep l3-a
12491 ?        Ss     7:39 l3-agent
/dev/pts/1
07:56:33
#which l3-agent
/usr/local/bin/l3-agent
/dev/pts/0
07:56:33
#which l3-agent
/usr/local/bin/l3-agent
/dev/pts/1
07:56:45
#cd /usr/local/bin/

/dev/pts/0
07:56:45
#cd /usr/local/bin/

/dev/pts/1
07:56:47
#cat l3-
cat: l3-: No such file or directory
/dev/pts/0
07:56:47
#cat l3-
cat: l3-: No such file or directory
/dev/pts/1
07:56:50
#cat 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
{
}
/dev/pts/0
07:56:50
#cat 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
{
}
/dev/pts/1
07:56:51
#less l3-agent
/dev/pts/0
07:56:51
#less l3-agent
/dev/pts/1
07:57:50
#cat l3-agent | less
/dev/pts/0
07:57:50
#cat l3-agent | less
/dev/pts/1
07:57:54
#head l3-agent
#!/usr/bin/perl -w
#
# (c) Igor Chubin, igor@chub.in, 2004-2008
#
use strict;
use POSIX;
use Term::VT102;
use Text::Iconv;
/dev/pts/0
07:57:54
#head l3-agent
#!/usr/bin/perl -w
#
# (c) Igor Chubin, igor@chub.in, 2004-2008
#
use strict;
use POSIX;
use Term::VT102;
use Text::Iconv;
07:57:59
#exit

07:58:27
$vim /etc/l
07:58:27
$vim /etc/lil
07:58:27
$vim /etc/lilalo/l3config.pm
08:00:00
$vim ~/.l3rc
08:00:18
$cd /tmp/

08:00:20
$l
итого 553
-rw-r--r--  1 root   root   120610 Сен 12 10:00 dcc2
drwxrwxrwt 13 root   root      680 Сен 12 10:00 ./
-rw-r--r--  1 root   root    76923 Сен 12 10:00 dcc1
-rw-r--r--  1 root   root        0 Сен 12 10:00 dayup_comp_all_full
-rw-rw----  1 root   root   101373 Сен 12 09:58 ytnef_cg_filter.log
drwxrwx---  3 root   root       60 Сен 12 09:58 ytnef_cg_filter/
-rw-r--r--  1 root   root      216 Сен 12 09:41 dat4
-rw-r--r--  1 root   root     3164 Сен 12 09:41 dat3
-rw-r--r--  1 root   root      228 Сен 12 09:38 dat2
...
srwxrwxr-x  1 root   root        0 Сен  8 15:46 .imspectoricqcookie
drwxr-xr-x  2 root   root       60 Сен  8 15:46 .winbindd/
lrwxrwxrwx  1 root   root       37 Сен  8 15:45 .s.PGSQL.5432 -> /var/lib/pgsql-root/tmp/.s.PGSQL.5432
-rw-r--r--  1 root   root        0 Сен  8 15:45 files.tmp
drwxrwxrwt  2 root   root       40 Сен  8 15:44 .esd/
drwxrwxrwt  2 root   root       40 Сен  8 15:44 .font-unix/
drwxrwxrwt  2 root   root       40 Сен  8 15:44 .ICE-unix/
drwxrwxrwt  2 root   root       40 Сен  8 15:44 .X11-unix/
drwxr-xr-x 24 root   root      680 Июл  5 13:25 ../
-rw-r--r--  1 su     su     151793 Мар 16  2008 lilalo.tar.gz
08:00:20
$cd lilalo

08:00:24
$l
итого 428
drwxrwxrwt 13 root root   680 Сен 12 10:00 ../
drwxr-xr-x  8 su   su     860 Сен  9 14:18 ./
-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
08:00:24
$ls
CVS      INSTALL      l3-config    l3scripts   lm-report  rebuild-all-logs
DELETE   l3-agent     l3config.pm  l3-upload   lm-ssh     share
doc      l3-backend   l3files      LICENSE     mysql      tail-all.pl
FILES    l3bashrc     l3-frontend  lilalo.kdi  nohup.out  tail-backend.pl
HISTORY  l3-cgi       l3prompt     lm          PM         taillast.pl
install  l3-cgi-lite  l3-report    lm-install  README     TODO
08:00:25
$less INSTALL
08:01:02
$./lm-
lm-install  lm-report   lm-ssh
08:01:02
$./lm-install
./lm-install [-d] path...
        * Use -d to deinstall labmaker
        * You can specify directory list to install LabMaker as command line parameters
          or set it in $users_to_install variable in the script
Example:
        Command
        # ./lm-install /root /home/user
        installs labmaker to /root and /home/user directories
08:01:10
$sudo su

/dev/pts/1
08:01:24
#./lm-install /root/ /home/su/ /home/kos_aa/
Don't forget to check .bash_profile for .bashrc call
LabMaker is installed to /root//.bash_profile
LabMaker is installed to /root//.bashrc
Don't forget to check .bash_profile for .bashrc call
LabMaker is installed to /home/su//.bash_profile
LabMaker is installed to /home/su//.bashrc
Don't forget to check .bash_profile for .bashrc call
LabMaker is installed to /home/kos_aa//.bash_profile
LabMaker is installed to /home/kos_aa//.bashrc
LabMaker is installed to /bin/vi
LabMaker is installed to /usr/bin/vim
/dev/pts/0
08:01:24
#./lm-install /root/ /home/su/ /home/kos_aa/
Don't forget to check .bash_profile for .bashrc call
LabMaker is installed to /root//.bash_profile
LabMaker is installed to /root//.bashrc
Don't forget to check .bash_profile for .bashrc call
LabMaker is installed to /home/su//.bash_profile
LabMaker is installed to /home/su//.bashrc
Don't forget to check .bash_profile for .bashrc call
LabMaker is installed to /home/kos_aa//.bash_profile
LabMaker is installed to /home/kos_aa//.bashrc
LabMaker is installed to /bin/vi
LabMaker is installed to /usr/bin/vim
/dev/pts/1
08:01:35
#./lm-report
Undefined subroutine &main::init_variables called at ./lm-report line 1162.
/dev/pts/0
08:01:35
#./lm-report
Undefined subroutine &main::init_variables called at ./lm-report line 1162.
08:01:46
#exit

Файлы

  • l3-agent
  • 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
    {
    }
    

    Статистика

    Время первой команды журнала07:34:01 2011- 9-12
    Время последней команды журнала08:01:46 2011- 9-12
    Количество командных строк в журнале46
    Процент команд с ненулевым кодом завершения, %13.04
    Процент синтаксически неверно набранных команд, % 0.00
    Суммарное время работы с терминалом *, час 0.46
    Количество командных строк в единицу времени, команда/мин 1.66
    Частота использования команд
    ps6|===========| 11.32%
    cat6|===========| 11.32%
    less5|=========| 9.43%
    grep4|=======| 7.55%
    vim4|=======| 7.55%
    cd4|=======| 7.55%
    lm-install3|=====| 5.66%
    l3|=====| 5.66%
    ls3|=====| 5.66%
    Find_CGP_dat_files2|===| 3.77%
    l32|===| 3.77%
    lm-report2|===| 3.77%
    exit2|===| 3.77%
    which2|===| 3.77%
    head2|===| 3.77%
    sudo1|=| 1.89%
    su1|=| 1.89%
    lm-1|=| 1.89%
    ____
    *) Интервалы неактивности длительностью 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$