lilalo
annotate lm @ 1:ff93ad94d73b
LiLaLo -- Live Lab Log
(L3)
Система автоматического ведения журналов
и автоматизированного управления ходом
лабораторных работ.
(L3)
Система автоматического ведения журналов
и автоматизированного управления ходом
лабораторных работ.
author | devi |
---|---|
date | Sun May 22 16:29:55 2005 +0300 (2005-05-22) |
parents | |
children | 6c1d2b9f45e7 |
rev | line source |
---|---|
devi@0 | 1 #!/usr/bin/perl |
devi@0 | 2 |
devi@0 | 3 |
devi@0 | 4 use strict; |
devi@0 | 5 use Inline::Files; |
devi@0 | 6 use Data::Dumper; |
devi@0 | 7 use Switch; |
devi@0 | 8 use XML::Simple; |
devi@0 | 9 use Getopt::Long; |
devi@0 | 10 |
devi@0 | 11 our $XMLClass; |
devi@0 | 12 our $XMLCourse; |
devi@0 | 13 our @Labs; |
devi@0 | 14 |
devi@0 | 15 our %Machines; # Machines list from class.xml |
devi@0 | 16 our @SelectedMachines; # Machines list given as the command line argument |
devi@0 | 17 |
devi@0 | 18 our $Config_File = "labmaker.conf"; |
devi@0 | 19 our %Config = ( |
devi@0 | 20 "show_host" => "no", |
devi@0 | 21 |
devi@0 | 22 # Каталоги |
devi@0 | 23 "path_labmaker" => "/var/labmaker/", |
devi@0 | 24 "path_classes" => "/var/labmaker/classes/", |
devi@0 | 25 "lablogs_path" => "/var/labmaker/lablogs/", |
devi@0 | 26 "courses_path" => "/var/labmaker/courses/", |
devi@0 | 27 "outpath" => "/var/labmaker/out/", |
devi@0 | 28 |
devi@0 | 29 # Файлы |
devi@0 | 30 "runfile" => "lm.run", |
devi@0 | 31 "logfile" => "lm.log", |
devi@0 | 32 |
devi@0 | 33 "class" => "class", # Имя файла класса |
devi@0 | 34 "class_suffix" => ".xml", # Cуффикс файла класса |
devi@0 | 35 |
devi@0 | 36 "sshkey" => "$ENV{HOME}/.ssh/id_dsa.pub", |
devi@0 | 37 "lmssh" => "./lm-ssh", |
devi@0 | 38 "lminstall" => "./lm-install", |
devi@0 | 39 "ssh_user" => "r", |
devi@0 | 40 ); |
devi@0 | 41 |
devi@0 | 42 our %Run = ( |
devi@0 | 43 "lab" => "" |
devi@0 | 44 ); |
devi@0 | 45 |
devi@0 | 46 our %Scripts; |
devi@0 | 47 |
devi@0 | 48 sub load_class; |
devi@0 | 49 sub load_config; |
devi@0 | 50 sub load_course; |
devi@0 | 51 sub load_scripts; |
devi@0 | 52 |
devi@0 | 53 sub lm_next; |
devi@0 | 54 sub lm_prev; |
devi@0 | 55 sub lm_start; |
devi@0 | 56 sub lm_stop; |
devi@0 | 57 sub lm_set; |
devi@0 | 58 sub lm_do; |
devi@0 | 59 sub lm_report; |
devi@0 | 60 sub lm_show_hosts; |
devi@0 | 61 sub lm_show_labs; |
devi@0 | 62 |
devi@0 | 63 sub load_run; |
devi@0 | 64 sub save_run; |
devi@0 | 65 sub print_log; |
devi@0 | 66 sub print_usage_info; |
devi@0 | 67 sub main(); |
devi@0 | 68 |
devi@0 | 69 main(); |
devi@0 | 70 |
devi@0 | 71 sub main() |
devi@0 | 72 { |
devi@0 | 73 binmode STDOUT, ":utf8"; |
devi@0 | 74 |
devi@0 | 75 if (! @ARGV) { |
devi@0 | 76 print_usage_info(); |
devi@0 | 77 exit(0); |
devi@0 | 78 } |
devi@0 | 79 |
devi@0 | 80 load_config; |
devi@0 | 81 load_run; |
devi@0 | 82 load_scripts; |
devi@0 | 83 load_class; |
devi@0 | 84 load_course; |
devi@0 | 85 |
devi@0 | 86 my $arg = join " ", @ARGV; |
devi@0 | 87 |
devi@0 | 88 # Getting @SelectedMachines if any |
devi@0 | 89 if ($arg =~ s/@(.*?)\s//) { |
devi@0 | 90 my $machines = $1; |
devi@0 | 91 my @list = split /,/, $machines; |
devi@0 | 92 for my $interval (@list) { |
devi@0 | 93 my ($first, $last) = split /-/, $interval; |
devi@0 | 94 |
devi@0 | 95 push @SelectedMachines, $first; |
devi@0 | 96 while ($first < $last) { |
devi@0 | 97 push @SelectedMachines, ++$first; |
devi@0 | 98 } |
devi@0 | 99 } |
devi@0 | 100 } |
devi@0 | 101 |
devi@0 | 102 # Choose command to do |
devi@0 | 103 switch ($arg) { |
devi@0 | 104 case "next" { lm_next } |
devi@0 | 105 case "prev" { lm_prev } |
devi@0 | 106 case /set / { $arg =~ /set (.*)/; lm_set $1 } |
devi@0 | 107 case "report" { lm_report } |
devi@0 | 108 case "start" { lm_start } |
devi@0 | 109 case "stop" { lm_stop } |
devi@0 | 110 case "show hosts" { lm_show_hosts } |
devi@0 | 111 case "show labs" { lm_show_labs } |
devi@0 | 112 case /do / { $arg =~ /do (.*)/; lm_do "$1" } |
devi@0 | 113 else { print_usage_info() } |
devi@0 | 114 } |
devi@0 | 115 save_run; |
devi@0 | 116 exit(0); |
devi@0 | 117 } |
devi@0 | 118 |
devi@0 | 119 sub load_scripts |
devi@0 | 120 { |
devi@0 | 121 local $/; |
devi@0 | 122 $_=<SCRIPTS>; |
devi@0 | 123 %Scripts = ("empty-element", split (/###(.*)\n/)); |
devi@0 | 124 delete($Scripts{"empty-element"}); |
devi@0 | 125 } |
devi@0 | 126 |
devi@0 | 127 sub load_config |
devi@0 | 128 { |
devi@0 | 129 my %file_config; |
devi@0 | 130 my %argv_config; |
devi@0 | 131 #read_config_file(\%file_config, $Config_File); |
devi@0 | 132 GetOptions(\%argv_config, map "$_=s", keys %Config); |
devi@0 | 133 %Config = (%Config, %file_config, %argv_config); |
devi@0 | 134 } |
devi@0 | 135 |
devi@0 | 136 sub load_course |
devi@0 | 137 { |
devi@0 | 138 $XMLCourse = XMLin($Config{"courses_path"}.$XMLClass->{"course"}.".xml", ForceArray => 1 ) |
devi@0 | 139 or die "Can't open file of the course ",$XMLClass->{"course"}," [with .xml extension]\n"; |
devi@0 | 140 # print Dumper($XMLCourse); |
devi@0 | 141 for my $lab (@{$XMLCourse->{"module"}}) { |
devi@0 | 142 push @Labs, $lab->{"code"}; |
devi@0 | 143 } |
devi@0 | 144 } |
devi@0 | 145 |
devi@0 | 146 sub load_class |
devi@0 | 147 { |
devi@0 | 148 my $classfile = |
devi@0 | 149 $Config{"path_classes"}."/".$Config{"class"}.$Config{"class_suffix"}; |
devi@0 | 150 $XMLClass = XMLin($classfile , ForceArray => [ 'student' ] ) |
devi@0 | 151 or die "Can't open file of the class ",$classfile,"\n"; |
devi@0 | 152 |
devi@0 | 153 for my $student (@{$XMLClass->{"student"}}) { |
devi@0 | 154 $Machines{$student->{"host"}} = { |
devi@0 | 155 "name" => "$student->{firstname} $student->{surname}", |
devi@0 | 156 "user" => "$student->{user}", |
devi@0 | 157 "student" => $student, |
devi@0 | 158 } |
devi@0 | 159 } |
devi@0 | 160 # print Dumper($XMLClass); |
devi@0 | 161 # print Dumper(\%Machines); |
devi@0 | 162 } |
devi@0 | 163 |
devi@0 | 164 |
devi@0 | 165 sub lm_next |
devi@0 | 166 { |
devi@0 | 167 for(my $i=0; $i<=$#Labs; $i++){ |
devi@0 | 168 if ( $Labs[$i] eq $Run{"lab"} ) { |
devi@0 | 169 if ($i < $#Labs) { |
devi@0 | 170 lm_set($Labs[$i+1]); |
devi@0 | 171 return ; |
devi@0 | 172 } else { |
devi@0 | 173 die "Lab ", $Run{"lab"}, " is the last. Switch to next lab is impossible" |
devi@0 | 174 } |
devi@0 | 175 } |
devi@0 | 176 |
devi@0 | 177 } |
devi@0 | 178 die "Lab ", $Run{"lab"}, " not found. Don't know which is next" |
devi@0 | 179 } |
devi@0 | 180 |
devi@0 | 181 sub lm_prev |
devi@0 | 182 # Switch to previous lab |
devi@0 | 183 { |
devi@0 | 184 for(my $i=0; $i<=$#Labs; $i++){ |
devi@0 | 185 if ( $Labs[$i] eq $Run{"lab"} ) { |
devi@0 | 186 if ($i > 0) { |
devi@0 | 187 lm_set($Labs[$i-1]); |
devi@0 | 188 return ; |
devi@0 | 189 } else { |
devi@0 | 190 die "Lab ", $Run{"lab"}, " is the first. Switch to previous lab is impossible" |
devi@0 | 191 } |
devi@0 | 192 } |
devi@0 | 193 |
devi@0 | 194 } |
devi@0 | 195 die "Lab ", $Run{"lab"}, " not found. Don't know which is previous" |
devi@0 | 196 } |
devi@0 | 197 |
devi@0 | 198 sub lm_set |
devi@0 | 199 # Switch to $_[0] lab |
devi@0 | 200 # FIXME |
devi@0 | 201 { |
devi@0 | 202 my $lab = shift; |
devi@0 | 203 print "Current lab is $lab\n"; |
devi@0 | 204 $Run{"lab"} = "$lab"; |
devi@0 | 205 lm_do "setlab", $lab; |
devi@0 | 206 } |
devi@0 | 207 |
devi@0 | 208 |
devi@0 | 209 sub lm_start |
devi@0 | 210 # Start new training day |
devi@0 | 211 { |
devi@0 | 212 print_log(`date`." STARTED\n"); |
devi@0 | 213 if ($Run{"lab"}) { |
devi@0 | 214 lm_next; |
devi@0 | 215 } |
devi@0 | 216 else |
devi@0 | 217 { |
devi@0 | 218 # First lab in the course |
devi@0 | 219 lm_set($Labs[0]); |
devi@0 | 220 } |
devi@0 | 221 } |
devi@0 | 222 |
devi@0 | 223 sub lm_stop |
devi@0 | 224 # Stop this training day |
devi@0 | 225 { |
devi@0 | 226 print_log(`date`." STOPPED\n"); |
devi@0 | 227 } |
devi@0 | 228 |
devi@0 | 229 |
devi@0 | 230 sub lm_show_hosts |
devi@0 | 231 # Show hosts used to run a commands |
devi@0 | 232 { |
devi@0 | 233 my $i=1; |
devi@0 | 234 for my $m (sort keys %Machines) { |
devi@0 | 235 if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) { |
devi@0 | 236 print "($i)","\t",$m,"\t",$Machines{$m}->{"name"},"\n"; |
devi@0 | 237 } |
devi@0 | 238 $i++; |
devi@0 | 239 } |
devi@0 | 240 } |
devi@0 | 241 |
devi@0 | 242 sub lm_show_labs |
devi@0 | 243 # Show hosts used to run a commands |
devi@0 | 244 { |
devi@0 | 245 my $i=1; |
devi@0 | 246 for my $lab (@Labs) { |
devi@0 | 247 print $lab; |
devi@0 | 248 print "*" if $lab eq $Run{"lab"}; |
devi@0 | 249 print "\n"; |
devi@0 | 250 } |
devi@0 | 251 } |
devi@0 | 252 |
devi@0 | 253 sub lm_do |
devi@0 | 254 # Do the $_[0] command on all of the hosts |
devi@0 | 255 { |
devi@0 | 256 my $command = shift; |
devi@0 | 257 my $arg = join " ", @_; |
devi@0 | 258 my $i=1; |
devi@0 | 259 for my $m (sort keys %Machines) { |
devi@0 | 260 if (!@SelectedMachines || grep $_ eq $i, @SelectedMachines) { |
devi@0 | 261 print "$m:\n" if $Config{"show_host"} =~ /y/i; |
devi@0 | 262 |
devi@0 | 263 my %myenv = ( %Config, |
devi@0 | 264 host => $m, |
devi@0 | 265 dirs => "/root /home/".$Machines{$m}->{"user"}, |
devi@0 | 266 lablogs => $Config{"lablogs_path"}."/". |
devi@0 | 267 $XMLClass->{"course"}."/". |
devi@0 | 268 $XMLClass->{"date"}."/". |
devi@0 | 269 "$m", |
devi@0 | 270 lab => $arg, |
devi@0 | 271 |
devi@0 | 272 email => $Machines{$m}->{"student"}->{"email"}, |
devi@0 | 273 company => $Machines{$m}->{"student"}->{"company"}, |
devi@0 | 274 center => $XMLClass->{"center"}, |
devi@0 | 275 course => $XMLClass->{"course"}, |
devi@0 | 276 date => $XMLClass->{"date"}, |
devi@0 | 277 name => $Machines{$m}->{"name"}, |
devi@0 | 278 coursepath => $XMLCourse->{"path"}, |
devi@0 | 279 |
devi@0 | 280 ); |
devi@0 | 281 if (grep { $_ eq $command} keys %Scripts) { |
devi@0 | 282 $_=$Scripts{"$command"}; |
devi@0 | 283 s/\$(\w+)/$myenv{$1}/ge; |
devi@0 | 284 open(SHELL, "|/bin/sh -s"); |
devi@0 | 285 binmode SHELL, ":utf8"; |
devi@0 | 286 print SHELL $_; |
devi@0 | 287 close (SHELL); |
devi@0 | 288 } |
devi@0 | 289 else { |
devi@0 | 290 my $res = `ssh $Config{"ssh_user"}\@$m $command`; |
devi@0 | 291 if ($res) { |
devi@0 | 292 my $count = ($res =~ s/(^)/$m: /mg); |
devi@0 | 293 print $res; |
devi@0 | 294 print "\n" if ($count > 1); |
devi@0 | 295 } |
devi@0 | 296 } |
devi@0 | 297 } |
devi@0 | 298 $i++; |
devi@0 | 299 } |
devi@0 | 300 } |
devi@0 | 301 |
devi@0 | 302 |
devi@0 | 303 sub lm_report |
devi@0 | 304 { |
devi@0 | 305 |
devi@0 | 306 print "Not implemented yet\n"; |
devi@0 | 307 exit(1); |
devi@0 | 308 |
devi@0 | 309 =cut comment |
devi@0 | 310 |
devi@0 | 311 Дальше идут скрипты, код которых нужно реализовать здесь. |
devi@0 | 312 Как минимум. |
devi@0 | 313 |
devi@0 | 314 =cut |
devi@0 | 315 |
devi@0 | 316 my $lm_script_make_report_web = <<'SCRIPT'; |
devi@0 | 317 |
devi@0 | 318 #!/bin/sh |
devi@0 | 319 |
devi@0 | 320 ####cd /home/devi/lm |
devi@0 | 321 ./lm do copy-lablogs |
devi@0 | 322 ##./lm-report --input /home/murk/.labmaker --output /var/www/lm/murk.html |
devi@0 | 323 ##exit |
devi@0 | 324 |
devi@0 | 325 COURSE=ug-h |
devi@0 | 326 DATE=2005-04-25 |
devi@0 | 327 SUFF=".linux.nt" |
devi@0 | 328 #MACHINES="m01 m02 m03 m04 m05 m06 m07 m08 m09 m10 m11 m12 m13 m14 m15" |
devi@0 | 329 #MACHINES="m1 m2 m3 m4 m5 m6 m7 f1 f2 f3 f4 f5" |
devi@0 | 330 MACHINES="m01 m02 m03 m04 m05 m06 m07 m08" |
devi@0 | 331 #MACHINES="m1" |
devi@0 | 332 USERS="user root" |
devi@0 | 333 WEBDIR=/var/www/lm |
devi@0 | 334 |
devi@0 | 335 for u in $USERS |
devi@0 | 336 do |
devi@0 | 337 for m in $MACHINES |
devi@0 | 338 do |
devi@0 | 339 e=utf-8 |
devi@0 | 340 [ "${m##f}" = "$m" ] || e=koi8-r |
devi@0 | 341 #e=koi8-r |
devi@0 | 342 mkdir -p $WEBDIR/$DATE/$m |
devi@0 | 343 cp share/*.ico share/*.css $WEBDIR/$m |
devi@0 | 344 #echo Processing Lablogs/$COURSE/$DATE/$m$SUFF/$u/ |
devi@0 | 345 ./lm-report\ |
devi@0 | 346 --input Lablogs/$COURSE/$DATE/$m$SUFF/$u/ \ |
devi@0 | 347 --diffs "Lablogs/$COURSE/$DATE/$m$SUFF/$u/ Lablogs/$COURSE/$DATE/$m$SUFF/root/"\ |
devi@0 | 348 --output /var/www/lm/$DATE/$m/$u.html \ |
devi@0 | 349 --encoding $e |
devi@0 | 350 done |
devi@0 | 351 done |
devi@0 | 352 |
devi@0 | 353 |
devi@0 | 354 SCRIPT |
devi@0 | 355 |
devi@0 | 356 my $lm_script_make_report_all = <<'SCRIPT'; |
devi@0 | 357 |
devi@0 | 358 #!/bin/sh |
devi@0 | 359 WEBDIR=/var/www/lm |
devi@0 | 360 |
devi@0 | 361 find . -type d -maxdepth 5 -mindepth 5 |\ |
devi@0 | 362 while read dir |
devi@0 | 363 do |
devi@0 | 364 subdir=${dir##Lablogs/} |
devi@0 | 365 e=utf-8 |
devi@0 | 366 echo $dir | grep -qi bsd && e=koi8-r |
devi@0 | 367 echo $dir | grep -qi /f && e=koi8-r |
devi@0 | 368 |
devi@0 | 369 mkdir -p $WEBDIR/${subdir%/*} |
devi@0 | 370 cp share/*.ico share/*.css $WEBDIR/${subdir%/*} |
devi@0 | 371 #echo Processing Lablogs/$COURSE/$DATE/$m$SUFF/$u/ |
devi@0 | 372 ./lm-report\ |
devi@0 | 373 --input $dir \ |
devi@0 | 374 --diffs "${dir%/user}/root $dir"\ |
devi@0 | 375 --output $WEBDIR/$subdir.html \ |
devi@0 | 376 --encoding $e |
devi@0 | 377 done |
devi@0 | 378 |
devi@0 | 379 SCRIPT |
devi@0 | 380 } |
devi@0 | 381 |
devi@0 | 382 sub load_run |
devi@0 | 383 { |
devi@0 | 384 my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"}; |
devi@0 | 385 open (RUN, $runfile) |
devi@0 | 386 or return; |
devi@0 | 387 while (<RUN>) { |
devi@0 | 388 chomp; |
devi@0 | 389 my ($var, $val) = split /\s+/,$_,2; |
devi@0 | 390 $Run{$var}=$val; |
devi@0 | 391 } |
devi@0 | 392 close (RUN); |
devi@0 | 393 } |
devi@0 | 394 |
devi@0 | 395 sub save_run |
devi@0 | 396 { |
devi@0 | 397 my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"}; |
devi@0 | 398 open (RN, "$runfile") |
devi@0 | 399 or die "Can't save running state to $runfile"; |
devi@0 | 400 for my $var (keys %Run) { |
devi@0 | 401 print RN $var,"\t",$Run{$var},"\n"; |
devi@0 | 402 } |
devi@0 | 403 close (RN); |
devi@0 | 404 } |
devi@0 | 405 |
devi@0 | 406 sub print_log |
devi@0 | 407 { |
devi@0 | 408 my $logfile = $Config{"path_labmaker"}."/".$Config{"path_logfile"}; |
devi@0 | 409 open (LOG, ">>$logfile") |
devi@0 | 410 or die "Can't open logfile $logfile for writing"; |
devi@0 | 411 print LOG @_; |
devi@0 | 412 close (LOG); |
devi@0 | 413 } |
devi@0 | 414 |
devi@0 | 415 |
devi@0 | 416 sub print_usage_info |
devi@0 | 417 { |
devi@0 | 418 print "Usage:\n\n\t$0 [host-list] command\n"; |
devi@0 | 419 while (<USAGE>) { |
devi@0 | 420 print $_; |
devi@0 | 421 } |
devi@0 | 422 } |
devi@0 | 423 |
devi@0 | 424 __USAGE__ |
devi@0 | 425 |
devi@0 | 426 Commands: |
devi@0 | 427 |
devi@0 | 428 next -- next lab |
devi@0 | 429 prev -- prev lab |
devi@0 | 430 set LAB -- set current lab to LAB |
devi@0 | 431 start -- start this day training |
devi@0 | 432 stop -- stop this day training |
devi@0 | 433 show hosts -- show available hosts in the class |
devi@0 | 434 show labs -- show available labs in the course |
devi@0 | 435 do COMMAND -- do specified command on the hosts of hostlist |
devi@0 | 436 report -- generate XML/HTML reports |
devi@0 | 437 |
devi@0 | 438 |
devi@0 | 439 do commands: |
devi@0 | 440 |
devi@0 | 441 install [PROFILE] -- install profile |
devi@0 | 442 |
devi@0 | 443 Host list: |
devi@0 | 444 |
devi@0 | 445 @N -- machine N |
devi@0 | 446 @N1-N2 -- all of the machines from N1 to N2 |
devi@0 | 447 @N1,N2,N3 -- machine N1, N2 and N3 |
devi@0 | 448 |
devi@0 | 449 N* is numbers or domain names of the machines. |
devi@0 | 450 |
devi@0 | 451 If host list is not specified, |
devi@0 | 452 command is executed on all of the machines |
devi@0 | 453 |
devi@0 | 454 |
devi@0 | 455 |
devi@0 | 456 __SCRIPTS__ |
devi@0 | 457 ###install |
devi@0 | 458 cat $sshkey | $lmssh $ssh_user@$host /bin/sh -c '"mkdir -p ~/.ssh; cat >>~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys"' |
devi@0 | 459 |
devi@0 | 460 ###install-lm |
devi@0 | 461 cat $lminstall | ssh $ssh_user@$host /bin/sh -s $dirs |
devi@0 | 462 |
devi@0 | 463 ###copy-lablogs |
devi@0 | 464 for i in $dirs |
devi@0 | 465 do |
devi@0 | 466 mkdir -p $lablogs/${i##*/} |
devi@0 | 467 scp -q $ssh_user@$host:${i}/.labmaker/* $lablogs/${i##*/} |
devi@0 | 468 done |
devi@0 | 469 |
devi@0 | 470 ###setlab |
devi@0 | 471 for i in $dirs |
devi@0 | 472 do |
devi@0 | 473 echo $lab | ssh $ssh_user@$host "cat > "${i}"/.labmaker/lab" |
devi@0 | 474 done |
devi@0 | 475 |
devi@0 | 476 ###makeout |
devi@0 | 477 common=$course-$date |
devi@0 | 478 personal=$course-$date-$email |
devi@0 | 479 mkdir -p $outpath/${common}/{Lablogs,Docs} |
devi@0 | 480 mkdir -p $outpath/${personal}/{Course,Files} |
devi@0 | 481 cd $outpath/${personal} |
devi@0 | 482 ln -s ../${common}/Lablogs . |
devi@0 | 483 ln -s ../${common}/Docs . |
devi@0 | 484 cd ~- |
devi@0 | 485 export UG_PERSONAL=${PWD}/$outpath/${personal}/Course |
devi@0 | 486 export UG_CENTER="$center" |
devi@0 | 487 export UG_COURSE="$course" |
devi@0 | 488 export UG_DATE="$date" |
devi@0 | 489 export UG_STUDENT="$name" |
devi@0 | 490 export UG_COMPANY="$company" |
devi@0 | 491 cd $coursepath; make personal; cd ~- |
devi@0 | 492 |
devi@0 | 493 ###watch |
devi@0 | 494 cat taillast.pl | ssh $ssh_user@$host perl - /root/.labmaker |