# HG changeset patch # User devi # Date 1116768595 -10800 # Node ID 18b21f6918f0fddcc53f671bbebaa57c26df48bb Initial revision diff -r 000000000000 -r 18b21f6918f0 .lm.swo Binary file .lm.swo has changed diff -r 000000000000 -r 18b21f6918f0 .lm.swp Binary file .lm.swp has changed diff -r 000000000000 -r 18b21f6918f0 .tarball --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.tarball Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,11 @@ +INSTALL +lm-install +lm-report +lm-report.conf +Makefile +.tarball +freebsd.ico +google.ico +opennet.ico +labmaker.css + diff -r 000000000000 -r 18b21f6918f0 FILES --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FILES Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,26 @@ +lm - labmaker's frontend + Отвечает за инсталляцию lm-агшентов, сбор данных для журналов, + вызов lm-report для генерирования журналов и т.д. + +lm-install - инсталлирует labmaker на машину. + Должен вызываться для тех пользователей, где необходимо вести запись + журналов действий + +lm-report - генератор отчётов labmaker + +lm-ssh - expect-скрипт, предназначенный для автоматический инсталляции + labmaker на удалённую машину посредством ssh. Автоматически + вводит пароль и отвечает 'yes' на вопрос об открытх ключах + хоста + +taillast.pl - показать хвост последнего изменённого журнала lm +ug2db - сконвертировать из UG Labmaker XML в DocBook XML + +labmaker.conf - конфигурационный файл программ lm и lm-report +share/ - файлы иконок, картинок, стилей + +FILES - список файлов в дистрибутиве LabMaker +INSTALL - описание процедуры инсталляции +README - информация о программе +TODO - список вещей, которые нужно сделать + diff -r 000000000000 -r 18b21f6918f0 INSTALL --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/INSTALL Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,9 @@ +Инсталляция LabMaker + +1. Проинсталлировать все необходимые модули Perl +2. Выполнить lm-install, указав в качестве параметров + имена домашних каталогов пользователей, для которых + должны вестись журналы +3. Использовать lm-report для генерирования журналов + + diff -r 000000000000 -r 18b21f6918f0 TODO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TODO Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,42 @@ +TODO LIST + +lm-report: + +general: Отметка фрагментов + якоря из командной строки + +user: Поддержка su и sudo +diff: FIX не показывать плавающий бокс +фрагменты: отмечать фрагменты для комментирования +html: управление деталями +html: навести порядок в css + +web: индекс активности +web: централизованный доступ +web: периодическое обновление +web: информация о лабе + + + +lm: + ls [[user@]host] + list [[user@]host] + показывает список ведщихся журналов + cat журнал + show журнал + показывает заданный журнал + + <журнал> указывается в форме console@host + + + save host + сохраняет журналы указанной машины + + + do выполнение команды + + стандартный набор команд (в т.ч. инсталляция) + +DONE + +xml: поддержка формата xml +html: заполлзающие блоки (ИСКЛЮЧЕНО) +general: Конфигурация через файл и командную строку diff -r 000000000000 -r 18b21f6918f0 labmaker.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/labmaker.conf Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,4 @@ +## Specify configurable parameters here +## in form +## variable = value +#terminal_width=100 diff -r 000000000000 -r 18b21f6918f0 lm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lm Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,494 @@ +#!/usr/bin/perl + + +use strict; +use Inline::Files; +use Data::Dumper; +use Switch; +use XML::Simple; +use Getopt::Long; + +our $XMLClass; +our $XMLCourse; +our @Labs; + +our %Machines; # Machines list from class.xml +our @SelectedMachines; # Machines list given as the command line argument + +our $Config_File = "labmaker.conf"; +our %Config = ( + "show_host" => "no", + + # Каталоги + "path_labmaker" => "/var/labmaker/", + "path_classes" => "/var/labmaker/classes/", + "lablogs_path" => "/var/labmaker/lablogs/", + "courses_path" => "/var/labmaker/courses/", + "outpath" => "/var/labmaker/out/", + + # Файлы + "runfile" => "lm.run", + "logfile" => "lm.log", + + "class" => "class", # Имя файла класса + "class_suffix" => ".xml", # Cуффикс файла класса + + "sshkey" => "$ENV{HOME}/.ssh/id_dsa.pub", + "lmssh" => "./lm-ssh", + "lminstall" => "./lm-install", + "ssh_user" => "r", +); + +our %Run = ( + "lab" => "" +); + +our %Scripts; + +sub load_class; +sub load_config; +sub load_course; +sub load_scripts; + +sub lm_next; +sub lm_prev; +sub lm_start; +sub lm_stop; +sub lm_set; +sub lm_do; +sub lm_report; +sub lm_show_hosts; +sub lm_show_labs; + +sub load_run; +sub save_run; +sub print_log; +sub print_usage_info; +sub main(); + +main(); + +sub main() +{ + binmode STDOUT, ":utf8"; + + if (! @ARGV) { + print_usage_info(); + exit(0); + } + + load_config; + load_run; + load_scripts; + load_class; + load_course; + + my $arg = join " ", @ARGV; + + # Getting @SelectedMachines if any + if ($arg =~ s/@(.*?)\s//) { + my $machines = $1; + my @list = split /,/, $machines; + for my $interval (@list) { + my ($first, $last) = split /-/, $interval; + + push @SelectedMachines, $first; + while ($first < $last) { + push @SelectedMachines, ++$first; + } + } + } + + # Choose command to do + switch ($arg) { + case "next" { lm_next } + case "prev" { lm_prev } + case /set / { $arg =~ /set (.*)/; lm_set $1 } + case "report" { lm_report } + case "start" { lm_start } + case "stop" { lm_stop } + case "show hosts" { lm_show_hosts } + case "show labs" { lm_show_labs } + case /do / { $arg =~ /do (.*)/; lm_do "$1" } + else { print_usage_info() } + } + save_run; + exit(0); +} + +sub load_scripts +{ + local $/; + $_=; + %Scripts = ("empty-element", split (/###(.*)\n/)); + delete($Scripts{"empty-element"}); +} + +sub load_config +{ + my %file_config; + my %argv_config; + #read_config_file(\%file_config, $Config_File); + GetOptions(\%argv_config, map "$_=s", keys %Config); + %Config = (%Config, %file_config, %argv_config); +} + +sub load_course +{ + $XMLCourse = XMLin($Config{"courses_path"}.$XMLClass->{"course"}.".xml", ForceArray => 1 ) + or die "Can't open file of the course ",$XMLClass->{"course"}," [with .xml extension]\n"; +# print Dumper($XMLCourse); + for my $lab (@{$XMLCourse->{"module"}}) { + push @Labs, $lab->{"code"}; + } +} + +sub load_class +{ + my $classfile = + $Config{"path_classes"}."/".$Config{"class"}.$Config{"class_suffix"}; + $XMLClass = XMLin($classfile , ForceArray => [ 'student' ] ) + or die "Can't open file of the class ",$classfile,"\n"; + + for my $student (@{$XMLClass->{"student"}}) { + $Machines{$student->{"host"}} = { + "name" => "$student->{firstname} $student->{surname}", + "user" => "$student->{user}", + "student" => $student, + } + } +# print Dumper($XMLClass); +# print Dumper(\%Machines); +} + + +sub lm_next +{ + for(my $i=0; $i<=$#Labs; $i++){ + if ( $Labs[$i] eq $Run{"lab"} ) { + if ($i < $#Labs) { + lm_set($Labs[$i+1]); + return ; + } else { + die "Lab ", $Run{"lab"}, " is the last. Switch to next lab is impossible" + } + } + + } + die "Lab ", $Run{"lab"}, " not found. Don't know which is next" +} + +sub lm_prev +# Switch to previous lab +{ + for(my $i=0; $i<=$#Labs; $i++){ + if ( $Labs[$i] eq $Run{"lab"} ) { + if ($i > 0) { + lm_set($Labs[$i-1]); + return ; + } else { + die "Lab ", $Run{"lab"}, " is the first. Switch to previous lab is impossible" + } + } + + } + die "Lab ", $Run{"lab"}, " not found. Don't know which is previous" +} + +sub lm_set +# Switch to $_[0] lab +# FIXME +{ + my $lab = shift; + print "Current lab is $lab\n"; + $Run{"lab"} = "$lab"; + lm_do "setlab", $lab; +} + + +sub lm_start +# Start new training day +{ + print_log(`date`." STARTED\n"); + if ($Run{"lab"}) { + lm_next; + } + else + { + # First lab in the course + lm_set($Labs[0]); + } +} + +sub lm_stop +# Stop this training day +{ + print_log(`date`." STOPPED\n"); +} + + +sub lm_show_hosts +# Show hosts used to run a commands +{ + my $i=1; + for my $m (sort keys %Machines) { + if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) { + print "($i)","\t",$m,"\t",$Machines{$m}->{"name"},"\n"; + } + $i++; + } +} + +sub lm_show_labs +# Show hosts used to run a commands +{ + my $i=1; + for my $lab (@Labs) { + print $lab; + print "*" if $lab eq $Run{"lab"}; + print "\n"; + } +} + +sub lm_do +# Do the $_[0] command on all of the hosts +{ + my $command = shift; + my $arg = join " ", @_; + my $i=1; + for my $m (sort keys %Machines) { + if (!@SelectedMachines || grep $_ eq $i, @SelectedMachines) { + print "$m:\n" if $Config{"show_host"} =~ /y/i; + + my %myenv = ( %Config, + host => $m, + dirs => "/root /home/".$Machines{$m}->{"user"}, + lablogs => $Config{"lablogs_path"}."/". + $XMLClass->{"course"}."/". + $XMLClass->{"date"}."/". + "$m", + lab => $arg, + + email => $Machines{$m}->{"student"}->{"email"}, + company => $Machines{$m}->{"student"}->{"company"}, + center => $XMLClass->{"center"}, + course => $XMLClass->{"course"}, + date => $XMLClass->{"date"}, + name => $Machines{$m}->{"name"}, + coursepath => $XMLCourse->{"path"}, + + ); + if (grep { $_ eq $command} keys %Scripts) { + $_=$Scripts{"$command"}; + s/\$(\w+)/$myenv{$1}/ge; + open(SHELL, "|/bin/sh -s"); + binmode SHELL, ":utf8"; + print SHELL $_; + close (SHELL); + } + else { + my $res = `ssh $Config{"ssh_user"}\@$m $command`; + if ($res) { + my $count = ($res =~ s/(^)/$m: /mg); + print $res; + print "\n" if ($count > 1); + } + } + } + $i++; + } +} + + +sub lm_report +{ + + print "Not implemented yet\n"; + exit(1); + +=cut comment + +Дальше идут скрипты, код которых нужно реализовать здесь. +Как минимум. + +=cut + + my $lm_script_make_report_web = <<'SCRIPT'; + +#!/bin/sh + +####cd /home/devi/lm +./lm do copy-lablogs +##./lm-report --input /home/murk/.labmaker --output /var/www/lm/murk.html +##exit + +COURSE=ug-h +DATE=2005-04-25 +SUFF=".linux.nt" +#MACHINES="m01 m02 m03 m04 m05 m06 m07 m08 m09 m10 m11 m12 m13 m14 m15" +#MACHINES="m1 m2 m3 m4 m5 m6 m7 f1 f2 f3 f4 f5" +MACHINES="m01 m02 m03 m04 m05 m06 m07 m08" +#MACHINES="m1" +USERS="user root" +WEBDIR=/var/www/lm + +for u in $USERS + do + for m in $MACHINES + do + e=utf-8 + [ "${m##f}" = "$m" ] || e=koi8-r + #e=koi8-r + mkdir -p $WEBDIR/$DATE/$m + cp share/*.ico share/*.css $WEBDIR/$m + #echo Processing Lablogs/$COURSE/$DATE/$m$SUFF/$u/ + ./lm-report\ + --input Lablogs/$COURSE/$DATE/$m$SUFF/$u/ \ + --diffs "Lablogs/$COURSE/$DATE/$m$SUFF/$u/ Lablogs/$COURSE/$DATE/$m$SUFF/root/"\ + --output /var/www/lm/$DATE/$m/$u.html \ + --encoding $e + done + done + + +SCRIPT + + my $lm_script_make_report_all = <<'SCRIPT'; + +#!/bin/sh +WEBDIR=/var/www/lm + +find . -type d -maxdepth 5 -mindepth 5 |\ + while read dir + do + subdir=${dir##Lablogs/} + e=utf-8 + echo $dir | grep -qi bsd && e=koi8-r + echo $dir | grep -qi /f && e=koi8-r + + mkdir -p $WEBDIR/${subdir%/*} + cp share/*.ico share/*.css $WEBDIR/${subdir%/*} + #echo Processing Lablogs/$COURSE/$DATE/$m$SUFF/$u/ + ./lm-report\ + --input $dir \ + --diffs "${dir%/user}/root $dir"\ + --output $WEBDIR/$subdir.html \ + --encoding $e + done + +SCRIPT +} + +sub load_run +{ + my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"}; + open (RUN, $runfile) + or return; + while () { + chomp; + my ($var, $val) = split /\s+/,$_,2; + $Run{$var}=$val; + } + close (RUN); +} + +sub save_run +{ + my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"}; + open (RN, "$runfile") + or die "Can't save running state to $runfile"; + for my $var (keys %Run) { + print RN $var,"\t",$Run{$var},"\n"; + } + close (RN); +} + +sub print_log +{ + my $logfile = $Config{"path_labmaker"}."/".$Config{"path_logfile"}; + open (LOG, ">>$logfile") + or die "Can't open logfile $logfile for writing"; + print LOG @_; + close (LOG); +} + + +sub print_usage_info +{ + print "Usage:\n\n\t$0 [host-list] command\n"; + while () { + print $_; + } +} + +__USAGE__ + +Commands: + + next -- next lab + prev -- prev lab + set LAB -- set current lab to LAB + start -- start this day training + stop -- stop this day training + show hosts -- show available hosts in the class + show labs -- show available labs in the course + do COMMAND -- do specified command on the hosts of hostlist + report -- generate XML/HTML reports + + +do commands: + + install [PROFILE] -- install profile + +Host list: + + @N -- machine N + @N1-N2 -- all of the machines from N1 to N2 + @N1,N2,N3 -- machine N1, N2 and N3 + + N* is numbers or domain names of the machines. + + If host list is not specified, + command is executed on all of the machines + + + +__SCRIPTS__ +###install +cat $sshkey | $lmssh $ssh_user@$host /bin/sh -c '"mkdir -p ~/.ssh; cat >>~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys"' + +###install-lm +cat $lminstall | ssh $ssh_user@$host /bin/sh -s $dirs + +###copy-lablogs +for i in $dirs +do + mkdir -p $lablogs/${i##*/} + scp -q $ssh_user@$host:${i}/.labmaker/* $lablogs/${i##*/} +done + +###setlab +for i in $dirs +do + echo $lab | ssh $ssh_user@$host "cat > "${i}"/.labmaker/lab" +done + +###makeout +common=$course-$date +personal=$course-$date-$email +mkdir -p $outpath/${common}/{Lablogs,Docs} +mkdir -p $outpath/${personal}/{Course,Files} +cd $outpath/${personal} +ln -s ../${common}/Lablogs . +ln -s ../${common}/Docs . +cd ~- +export UG_PERSONAL=${PWD}/$outpath/${personal}/Course +export UG_CENTER="$center" +export UG_COURSE="$course" +export UG_DATE="$date" +export UG_STUDENT="$name" +export UG_COMPANY="$company" +cd $coursepath; make personal; cd ~- + +###watch +cat taillast.pl | ssh $ssh_user@$host perl - /root/.labmaker diff -r 000000000000 -r 18b21f6918f0 lm-install --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lm-install Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,227 @@ +#!/bin/sh + +# 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 + +# CONFIGURABLE SECTION start +#users_to_install="/home/your-user-here /root" +# CONFIGURABLE SECTION stop + +first_lab=T1 +editors_to_install='/bin/vi /usr/bin/vi /usr/bin/vim /bin/ee /usr/bin/ee /usr/bin/pico /usr/bin/nano /usr/local/bin/vim' +temp_file=/tmp/lm-install-$$ +arg=$@ + +show_usage() +{ + cat << USAGE + +$0 [-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 + # $0 /root /home/user + installs labmaker to /root and /home/user directories + +USAGE +} + +install_to_profile() +{ + profile=$1 + cat $profile \ + | sed '/LabMaker:START/,/LabMaker:END/ d' \ + > $temp_file + cat <<'LM_bash_profile' >> $temp_file + # LabMaker:START + #LMHOME=~/.labmaker + #mkdir -p ${LMHOME} + #TTY=`tty` + #flush="-f" + #exec script $flush -q $LMHOME/${TTY##*/}-$$.script + # LabMaker:END +LM_bash_profile + cat $temp_file > $profile + rm $temp_file +} + +uninstall_from_profile() +{ + profile=$1 + cat $profile \ + | sed '/LabMaker:START/,/LabMaker:END/ d' \ + > $temp_file + cat $temp_file > $profile + rm $temp_file +} + +install_to_bashrc() +{ + profile=$1 + cat $profile \ + | sed '/LabMaker:START/,/LabMaker:END/ d' \ + > $temp_file + cat <<'LM_bash_profile' >> $temp_file + # LabMaker:START + TTY=`tty` + + this_term=`w | grep "${TTY##/dev/}" | awk '{print $8;}'` + # freeBSD: + this_term=`w | grep "${TTY##/dev/tty}" | awk '{print $6;}'` + if [ -n "$this_term" ] && echo $this_term | grep -qv script + then + LMHOME=~/.labmaker + mkdir -p ${LMHOME} + flush="-f" #linux + flush="-t 0" #freebsd + exec script $flush -q $LMHOME/${TTY##*/}-$$.script + fi + PS1='\[` a="$?"; + HIDDEN=$([ "$a" = 0 ] || echo -n ^"$a")$(echo -n _${UID}_)$(echo -n _$$_)$(date\ + +"%j$(cat ~/.labmaker/lab 2>/dev/null) %H:%M:%S"); + echo $HIDDEN`\033[50D\033[K\][\u@\h:\W]\$ ' + # LabMaker:END +LM_bash_profile + cat $temp_file > $profile + rm $temp_file +} + +uninstall_from_bashrc() +{ + profile=$1 + cat $profile \ + | sed '/LabMaker:START/,/LabMaker:END/ d' \ + > $temp_file + cat $temp_file > $profile + rm $temp_file +} + +install_editor() +{ +editor=$1 +[ -e $editor.orig ] && cp $editor.orig $editor +cp $editor $editor.orig +cat <<'editor_wrapper' | sed "s@EDITOR@$editor@" > $editor +#!/bin/sh + +LMHOME=~/.labmaker +if [ "${1#-}" = "$1" -a -d "$LMHOME" ] +then + LAB=`cat $LMHOME/lab` + TIME="`date +%j${LAB}_%H:%M:%S`" + DIR="" + [ "${1#/}" = "$1" ] && DIR=$PWD/ + DIFFNAME=$PPID_${TIME}_`echo $DIR$1| sed s@_@__@ | sed 's@/@_@g'`.diff + tmp="/tmp/lm-saved-$$" + touch $1 + cp -- "$1" $tmp 2> /dev/null + EDITOR.orig "$@" || ERR=1 + diff $tmp $1 > $LMHOME/$DIFFNAME 2> /dev/null + rm $tmp 2> /dev/null + if [ "$ERR" = 1 ] + then + false + else + true + fi +else + exec EDITOR.orig "$@" +fi +editor_wrapper + +} + + +uninstall_editor() +{ + editor=$1 + [ -e $editor.orig ] && mv $editor.orig $editor +} + +if [ "$1" != "-d" ] +then + # INSTALLING LM + if [ $# -gt 0 ] + then + users_to_install="$*" + fi + + if [ -z "$users_to_install" ] + then + show_usage + exit + fi + + for user in $users_to_install + do + home=$user # fix this! + mkdir -p $home/.labmaker + echo $first_lab > $home/.labmaker/lab + chown -R ${user##*/} $home/.labmaker + + if [ ! -e $home/.bash_profile ] + then + echo '. ~/.bashrc' >> ~/.bash_profile + fi + [ -e $home/.bash_profile ] \ + && install_to_profile $home/.bash_profile \ + && echo LabMaker is installed to $home/.bash_profile + + [ -e $home/.profile ] && install_to_profile $home/.profile \ + && install_to_profile $home/.profile \ + && echo LabMaker is installed to $home/.profile + + touch $home/.bashrc + [ -e $home/.bashrc ] && install_to_bashrc $home/.bashrc \ + && install_to_bashrc $home/.bashrc \ + && echo LabMaker is installed to $home/.bashrc + done + + for editor in $editors_to_install + do + [ -e $editor ] \ + && install_editor $editor \ + && echo LabMaker is installed to $editor + done +else +# UNINSTALLING LM + shift + users_to_install="$*" + for user in $users_to_install + do + home=$user + mkdir -p $home/.labmaker + echo $first_lab > $home/.labmaker/lab + chown -R ${user##*/} $home/.labmaker + + if [ ! -e $home/.bash_profile ] + then + echo '. ~/.bashrc' >> ~/.bash_profile + fi + [ -e $home/.bash_profile ] \ + && uninstall_from_profile $home/.bash_profile \ + && echo LabMaker is uninstalled from $home/.bash_profile + + [ -e $home/.profile ] && uninstall_from_profile $home/.profile \ + && uninstall_from_profile $home/.profile \ + && echo LabMaker is uninstalled from $home/.profile + + touch $home/.bashrc + [ -e $home/.bashrc ] && uninstall_from_bashrc $home/.bashrc \ + && uninstall_from_bashrc $home/.bashrc \ + && echo LabMaker is uninstalled from $home/.bashrc + done + + for editor in $editors_to_install + do + [ -e $editor ] \ + && uninstall_editor $editor \ + && echo LabMaker is uninstalled from $editor + done +fi diff -r 000000000000 -r 18b21f6918f0 lm-report --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lm-report Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,912 @@ +#!/usr/bin/perl -w + +# +# (c) Igor Chubin, imchubin@mail.ru, 2004 +# + +use strict; +use Getopt::Long; +use Term::VT102; +use Text::Iconv; +use Data::Dumper; + +our $Config_File = "labmaker.conf"; +our %Config = ( + "skip_empty" => "yes", + "skip_interrupted" => "no", + "skip_wrong" => "no", + "editors" => ["vi", "pico", "ee", "vim"], + "pagers" => ["more", "less", "zmore", "zless", "info", + "man", "mc", "trafshow", "screen", "cfdisk", + "trafshow-bsd", "yes", "lynx", "links", "centericq" + ], + "terminal" => ["mc"], + "suppress_editors" => "yes", + "suppress_pagers" => "yes", + "suppress_terminal" => "yes", + + "terminal_width" => 100, + "terminal_height" => 100, + "verbose" => "yes", + + "head_lines" => 5, + "tail_lines" => 5, + "skip_text" => "...", + "show_time" => "yes", + "show_diffs" => "yes", + "show_comments" => "yes", + + "input" => "/root/.labmaker", + "diffs" => "", + "input_mask" => "*.script", + "encoding" => "utf-8", + + "output" => "/var/www/lm/reportINDEX.html", + #"output" => "report.xml", + "output_mask" => "INDEX", + "output_format" => "html", + + "signature" => "#lm:", + "from" => "", + "to" => "", + "lab" => "", + "keywords" => "linux command", + "files_keywords" => "linux file", + + comment_width => "300", + time_width => "60", + + ); + +our @Command_Lines; +our @Command_Lines_Index; +our @Diffs; + +our %Commands_Stat; # Statistics about commands usage +our %Files_Stat; # Statistics about commands usage + +our %Search_Machines = ( + "google" => { "query" => "http://www.google.com/search?q=" , + "icon" => "google.ico" }, + "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", + "icon" => "freebsd.ico" }, + "linux" => { "query" => "http://man.he.net/?topic=", + "icon" => "linux.ico"}, + "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=", + "icon" => "opennet.ico"}, + "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", + "icon" => "freebsd.ico" }, + + ); + +our %Elements_Visibility = ( + "note" => "замечания", + "diff" => "редактор", + "time" => "время", + "ttychange" => "терминал", + "wrong_output wrong_cline wrong_root_output wrong_root_cline" + => "команды с ошибками", + "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" + => "прерванные команды", + "tab_completion_output tab_completion_cline" + => "продолжение с помощью tab" +); + + +sub load_diff_files +{ + my @pathes = @_; + + for my $path (@pathes) { + my $template = "*.diff"; + my @files = <$path/$template>; + my $i=0; + for my $file (@files) { + my %diff; + + $diff{"path"}=$path; + $diff{"uid"}="SET THIS"; + +# Сейчас UID определяется из названия каталога +# откуда берутся diff-файлы +# Это неправильно +# +# ВАРИАНТ: +# К файлам жураналам должны прилагаться ситемны файлы, +# мз которых и будет определяться соответствие +# имён пользователей их uid'ам +# + $diff{"uid"} = 0 if $path =~ m@/root/@; + + $diff{"bind_to"}=""; + $diff{"time_range"}=-1; + + next if not $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{"index"}=$i; + + print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n"; + + local $/; + open (F, "$file") + or return "Can't open file $file ($_[0]) for reading"; + my $text = ; + 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; + #print "$file loaded ($diff{day})\n"; + + push @Diffs, \%diff; + $i++; + } + } +} + + +sub bind_diff +{ +# my $path = shift; +# my $pid = shift; +# my $day = shift; +# my $lab = shift; + + 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; + + for my $diff (@Diffs) { + # Check here date, time and user + next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"})); + #next if (!$diff->{"uid"} && $cl->{"euid"} != $diff->{"uid"}); + + my $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec); + if ($dt >0 && $dt < $min_dt && ($diff->{"time_range"} <0 || $dt < $diff->{"time_range"})) { + print "Approppriate diff found: dt=$dt\n"; + if ($diff->{"bind_to"}) { + undef $diff->{"bind_to"}->{"diff"}; + }; + $diff->{"time_range"}=$dt; + $diff->{"bind_to"}=$cl; + + $cl->{"diff"} = $diff->{"index"}; + $min_dt = $dt; + } + + } +} + + +sub extract_from_cline +# Разобрать командную строку $_[1] и возвратить хэш, содержащий +# номер первого появление команды в строке: +# команда => первая позиция +{ + my $what = $_[0]; + my $cline = $_[1]; + 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++; + }; + if ($2) { + my $args = $2; + my @args = split (/\s+/, $args); + for my $a (@args) { + $files{"$a"}=$i++ + if !defined $files{"$a"}; + }; + + + } + } + + if ($what eq "commands") { + return %commands; + } else { + return %files; + } + +} + +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(?:$cline_re_base|$)/x; + #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x; + my $cline_re = qr/$cline_re_base/sx; + my $cline_re1 = qr/$cline_re_base\x0D/sx; + my $cline_re2 = qr/$cline_re_base$/sx; + + my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"}, + 'rows' => $Config{"terminal_height"}); + 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 "Loading lm-scripts...\n" if $Config{"verbose"} =~ /y/; + + my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>; + my $file; + my $files_number = $#lab_scripts; + my $ii = 0; + my $skip_info; + + my $commandlines_loaded =0; + my $commandlines_processed =0; + + for $file (@lab_scripts){ + #printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/; + + open (FILE, "$file"); + binmode FILE; + $file =~ m@.*/(.*?)-.*@; + + my $tty = $1; + my $first_pass = 1; + my %cl; + my $last_output_length=0; + while () { + $commandlines_processed++; + # time + + 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)//; + # print "!!!",$_,"!!!\n"; + # next; + # while (m/$cline_re1/gs) { + # } + m/$cline_re2/gs; + + $commandlines_loaded++; + $last_output_length=0; + + # Previous command + my %last_cl = %cl; + my $err = $1 || ""; + + # Parse new command + $cl{"uid"} = $2; + $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0 + $cl{"pid"} = $3; + $cl{"day"} = $4; + $cl{"lab"} = $5; + $cl{"hour"} = $6; + $cl{"min"} = $7; + $cl{"sec"} = $8; + $cl{"fullprompt"} = $9; + $cl{"prompt"} = $10; + $cl{"raw_cline"} = $11; + + $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*$//; + $cline_vt->reset(); + + my %commands = extract_from_cline("commands", $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"}; + + # Processing previous command line + if ($first_pass) { + $first_pass = 0; + next; + } + + # Error code + $last_cl{"err"}=$err; + $last_cl{"err"}=130 if $err eq "^C"; + + if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) { + bind_diff(\%last_cl); + } + + # Output + if (!$last_cl{"suppress_output"} || $last_cl{"err"}) { + for (my $i=0; $i<$Config{"terminal_height"}; $i++) { + my $line= $vt->row_plaintext($i); + next if !defined ($line) || $line =~ /^\s*$/; + $line =~ s/\s*$//; + $last_cl{"output"} .= $line."\n"; + } + } + else { + $last_cl{"output"}= ""; + } + + $vt->reset(); + + + # Classifying the command line + + + # Save + if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) { + # Changing encoding + for (keys %last_cl) { + $last_cl{$_} = $converter->convert($last_cl{$_}) + if ($Config{"encoding"} && + $Config{"encoding"} !~ /^utf-8$/i); + } + push @Command_Lines, \%last_cl; + } + next; + } + $last_output_length+=length($_); + #if (!$cl{"suppress_output"} || $last_output_length < 5000) { + if ($last_output_length < 5000) { + #print "(",length($_),")" if (length($_) > 2000) ; + $vt->process("$_"."\n") + } + else + { + if (!$skip_info) { + print "($cl{last_command})"; + $skip_info = 1; + } + } + } + close(FILE); + + } + if ($Config{"verbose"} =~ /y/) { + print "...finished." ; + print "Lines loaded: $commandlines_processed\n"; + print "Command lines: $commandlines_loaded\n"; + } +} + +sub search_by +{ + my $sm = shift; + my $topic = shift; + $topic =~ s/ /+/; + + return ""; +} + +sub make_comment +{ + my $commands = $_[0]; + my $files = $_[1]; + chomp $commands; + chomp $files; + return if (!$commands && !$files); + + my $comment=""; + + # Commands + for my $command (split /\s+/,$commands) { + $command =~ s/'//g; + my $description=""; + eval { $description=`mywi-client '$command'`; } ; + $description = join ("
\n", grep(/\([18]\)/, split(/\n/, $description))); + $description =~ s/.*?-//; + next if $description =~ /^\s*$/; + + my $query=$command." ".$Config{"keywords"}; + $query =~ s/\ /+/g; + my $search= search_by("opennet",$query). + search_by("local",$command). + search_by("google",$query); + + $comment .= "$command". + "$search". + "". + "$description"; + } + + # Files + for my $file (split /\s+/,$files) { + $file =~ s@.*/@@; + $file =~ s/'//g; + next if $file =~ /^\s*$/; + next if $file =~ /^-/; + + my $description=`mywi '$file'`; + $description = join ("
\n", grep(/\(5\)/, split(/\n/, $description))); + next if $description =~ /^\s*$/; + + my $query=$file." ".$Config{"files_keywords"}; + $query =~ s/\ /+/g; + my $search= search_by("opennet",$query). + search_by("local",$file). + search_by("google",$query); + + $comment .= "$file". + "$search". + "". + "$description"; + } + + + return $comment; +} + +sub printq +{ + my $TO = shift; + my $text = join "", @_; + $text =~ s/&/&/g; + $text =~ s//>/g; + print $TO $text; +} + + +sub sort_command_lines +{ + print "Sorting command lines...\n" 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 { + $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"} + } @index; + + print "...finished\n" if $Config{"verbose"} =~ /y/; + +} + +sub process_command_lines +{ + my $lab_scripts_path = $_[0]; + + for my $i (@Command_Lines_Index) { + + my $cl = \$Command_Lines[$i]; + @{${$cl}->{"new_commands"}} =(); + @{${$cl}->{"new_files"}} =(); + $$cl->{"class"} = ""; + + if ($$cl->{"err"}) { + $$cl->{"class"}="wrong"; + $$cl->{"class"}="interrupted" + if ($$cl->{"err"} eq 130); + } + if (!$$cl->{"euid"}) { + $$cl->{"class"}.="_root"; + } + +#tab# my @tab_words=split /\s+/, $$cl->{"output"}; +#tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/; +#tab# $last_word =~ s@.*/@@; +#tab# my $this_is_tab=1; +#tab# +#tab# if ($last_word && @tab_words >2) { +#tab# for my $tab_words (@tab_words) { +#tab# if ($tab_words !~ /^$last_word/) { +#tab# $this_is_tab=0; +#tab# last; +#tab# } +#tab# } +#tab# } +#tab# $$cl->{"class"}="tab" if $this_is_tab; + + + if ( !$$cl->{"err"}) { + # Command does not contain mistakes + + my %commands = extract_from_cline("commands", ${$cl}->{"cline"}); + my %files = extract_from_cline("files", ${$cl}->{"cline"}); + + # Searching for new commands only + for my $command (keys %commands) { + if (!defined $Commands_Stat{$command}) { + push @{$$cl->{new_commands}}, $command; + } + $Commands_Stat{$command}++; + } + + for my $file (keys %files) { + if (!defined $Files_Stat{$file}) { + push @{$$cl->{new_files}}, $file; + } + $Files_Stat{$file}++; + } + } + } + +} + +sub print_command_lines +{ + my $output_filename=$_[0]; + my $format = $Config{"output_format"}; + + open(OUT, ">", $output_filename) + or die "Can't open $output_filename for writing\n"; + + + + if ($format eq "html") { + # vvvv HTML Header + print OUT < + + + + + + +HEADER + print OUT "
\n"; + for my $element (keys %Elements_Visibility) + { + my @e = split /\s+/, $element; + my $showhide = join "", map { "ShowHide('$_');" } @e ; + print OUT "", + $Elements_Visibility{$element}, + "
\n"; + } + #print OUT "". + #"
\n"; + #print OUT "". + #"
\n"; + + print OUT "
\n"; + print OUT "\n"; + # ^^^^ HTML Header + } + else { + # XML Header + print OUT "\n"; + } + close(OUT); +} + +sub read_config_file +{ + my $config = $_[0]; + my $filename = $_[1]; + open(CONFIG, "$filename") + or return; + while () { + s/#.*//; + next if /^\s*$/; + my ($var, $val) = split /\s*=\s*/, $_, 2; + $var =~ s/\s*//; + $config->{$var} = $val; + } + close(CONFIG); +} + +$| = 1; + +my %file_config; +my %argv_config; +read_config_file(\%file_config, $Config_File); +GetOptions(\%argv_config, map "$_=s", keys %Config); +%Config = (%Config, %file_config, %argv_config); + + +my $i=0; + +for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) +{ + load_diff_files($lab_log); +} + +for my $lab_log (split /\s+/, $Config{"input"}) +{ + my $tofile=$Config{"output"}; + $tofile =~ s/$Config{"output_mask"}/$i/; + #load_diff_files($lab_log); + load_command_lines($lab_log, $Config{"input_mask"}); + sort_command_lines; + process_command_lines($lab_log); + print_command_lines($tofile); + $i++; +} + diff -r 000000000000 -r 18b21f6918f0 lm-ssh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lm-ssh Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,31 @@ +#!/bin/sh +#\ +exec expect -- "$0" ${1+"$@"} +eval spawn ssh $argv +set timeout 1 +expect { + eof { + } + "(yes/no)? " { + send "yes\n" + exp_continue + } + assword: { + send "rootpass\n" + } + timeout { + interact -nobuffer + } +} +expect { + eof { + } + assword: { + send "rootpass\n" + } + + timeout { + interact -nobuffer + } +} + diff -r 000000000000 -r 18b21f6918f0 share/freebsd.ico Binary file share/freebsd.ico has changed diff -r 000000000000 -r 18b21f6918f0 share/google.ico Binary file share/google.ico has changed diff -r 000000000000 -r 18b21f6918f0 share/labmaker.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/labmaker.css Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,89 @@ +.cline,.wrong_cline,.interrupted_cline,.output,.wrong_output,.interrupted_output,.tab_cline, ._root_cline,.wrong_root_cline,.interrupted_root_cline,._root_output,.wrong_root_output,.interrupted_root_output,.tab_root_cline + { + margin : 0px 0px 0px 0px; + padding : 0px 0px 0px 10px; + vertical-align : top; + } +.cline,.wrong_cline,.interrupted_cline,.tab_cline,._root_cline,.wrong_root_cline,.interrupted_root_cline,.tab_root_cline + { font-weight : bold; } +.wrong_output,.wrong_root_output + { color : #cc6666; } +.interrupted_output,.interrupted_root_output + { color : #aaaaaa; } +.wrong_cline, .wrong_root_cline + { color : #ee7777; } +.interrupted_cline, .interrupted_root_cline + { color : #777777; } + +._root_cline,.wrong_root_cline,.interrupted_root_cline,._root_output,.wrong_root_output,.interrupted_root_output,.tab_root_cline + { + border-left : #ff0000 solid thin; + + } + +.note { + color : black; + background : #d8fcff; + margin : 12px 12px 12px 12px; + padding : 6px 6px 6px 6px; + border-style : dashed; + border-width : thin; + border-color : #a8eaff; + vertical-align : top; +} + +.note_title,.note_text,.note_search + { + color : black; + margin : 0px 0px 0px 0px; + padding : 0px 0px 0px 0px; + vertical-align : top; + } +.note_title { font-family : sans-serif; } +.note_text { font-size : 80%; } +.note_search { text-align : right; } + + +.diff { + color : black; + background : #fdffcd; + margin : 16px 16px 16px 16px; + padding : 6px 6px 6px 6px; + border-style : dashed; + border-width : thin; +} + +.ttychange { + color : #9a9a9a; + background : #e7e7e7; + margin : 0px 0px 0px 0px; + padding : 0px 0px 0px 0px; + vertical-align : top; + font-family : monospace; +} + +.time { + color : #999999; + margin : 0px 0px 0px 0px; + padding : 0px 10px 0px 0px; + vertical-align : top; + font-size : 80%; + vertical-align : top; + /* border-right : #9a9a9a solid thin; */ +} + +.script { + margin : 0px 0px 0px 0px; + padding : 0px 0px 0px 0px; + vertical-align : top; +} + +.visibility_form { + color : #9a9a9a; + background : #e7e7e7; + margin : 0px 0px 0px 0px; + padding : 0px 0px 0px 0px; + vertical-align : top; + font-size : 80%; + font-family : sans-serif; +} diff -r 000000000000 -r 18b21f6918f0 share/opennet.ico Binary file share/opennet.ico has changed diff -r 000000000000 -r 18b21f6918f0 share/ug.xsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ug.xsl Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 000000000000 -r 18b21f6918f0 taillast.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/taillast.pl Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,8 @@ +#!/usr/bin/perl + +for $d (@ARGV) { + push @d, glob("$d/*"); +} +@{f} = sort { (stat(${a}))[9] <=> (stat(${b}))[9] } (@d); +print "tail -f ${f[$#f]}\n"; +__END__ diff -r 000000000000 -r 18b21f6918f0 ug2db --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ug2db Sun May 22 16:29:55 2005 +0300 @@ -0,0 +1,7 @@ +#!/bin/sh + +from=$1 +to=$2 +xsl=ug.xsl +xsltproc -o $to $xsl $from +