lilalo

changeset 69:1e1422588716

l3-agent, l3-frontend:

1)
Добавилась поддержка автоматического выкусывания файлов.
По-видимому нужно сделать чтобы автоматически вы журнале
показывались только самые вкусные файлы,
отмеченные знаком
#!
Остальные же чтобы можно было посмотреть по ссылке


2)
Форма управления видимостью элементов
находится в правом верхнем углу экрана,
и не прокручивается по мере прокрутки журнала.
Надо ещё будет сделать чтобы можно было её прятать.

lm:
Добавил
lm show email
author devi
date Tue Jan 31 20:18:54 2006 +0200 (2006-01-31)
parents 8c3d80c4891b
children 6f5ad37fc8d9
files l3-agent l3-frontend l3config.pm lm
line diff
     1.1 --- a/l3-agent	Sat Jan 28 09:00:25 2006 +0200
     1.2 +++ b/l3-agent	Tue Jan 31 20:18:54 2006 +0200
     1.3 @@ -488,7 +488,10 @@
     1.4          # Вырезаем из вывода только нужное количество строк
     1.5  
     1.6          my $output="";
     1.7 -        if ($Config{"head_lines"} || $Config{"tail_lines"}) {
     1.8 +
     1.9 +        if (!grep ($_ eq $cl{"last_command"}, @{$Config{"full_output_commands"}})
    1.10 +            && ($Config{"head_lines"} 
    1.11 +            || $Config{"tail_lines"})) 
    1.12              # Partialy output
    1.13              my @lines = split '\n', $cl->{"output"};
    1.14              # head
     2.1 --- a/l3-frontend	Sat Jan 28 09:00:25 2006 +0200
     2.2 +++ b/l3-frontend	Tue Jan 31 20:18:54 2006 +0200
     2.3 @@ -12,6 +12,8 @@
     2.4  our $Mywi_Socket;
     2.5  our %Sessions;
     2.6  
     2.7 +our %Files;
     2.8 +
     2.9  # vvv Инициализация переменных выполняется процедурой init_variables
    2.10  our @Day_Name;
    2.11  our @Month_Name;
    2.12 @@ -38,6 +40,7 @@
    2.13  
    2.14  sub print_all;
    2.15  sub print_command_lines;
    2.16 +sub print_files;
    2.17  sub print_stat;
    2.18  sub print_header;
    2.19  sub print_footer;
    2.20 @@ -498,8 +501,16 @@
    2.21  # и <tail_lines> нижних строк,
    2.22  # если эти параметры существуют
    2.23  
    2.24 +        if ($cl->{"last_command"} eq "cat" && !$cl->{"err"} && !($cl->{"cline"} =~ /</)) {
    2.25 +            my $filename = $cl->{"cline"};
    2.26 +            $filename =~ s/.*\s+(\S+)\s*$/$1/;
    2.27 +            $Files{$filename} = $cl->{"output"};
    2.28 +        }
    2.29          my @lines = split '\n', $cl->{"output"};
    2.30 -        if (($Config{"head_lines"} || $Config{"tail_lines"})
    2.31 +        if ((
    2.32 +             $Config{"head_lines"} 
    2.33 +             || $Config{"tail_lines"}
    2.34 +             )
    2.35               && $#lines >  $Config{"head_lines"} + $Config{"tail_lines"} ) {
    2.36  
    2.37              for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
    2.38 @@ -513,7 +524,7 @@
    2.39              }
    2.40          } 
    2.41          else {
    2.42 -            $output .= $cl->{"output"};
    2.43 +           $ output .= $cl->{"output"};
    2.44          }   
    2.45  
    2.46  #
    2.47 @@ -737,9 +748,10 @@
    2.48          my $hint;
    2.49          for my $c (reverse sort { $CommandsFDistribution{$a} <=> $CommandsFDistribution{$b} } @new_commands) {
    2.50                  $hint = make_comment($c);
    2.51 +                next unless $hint;
    2.52                  my ($command, $hint) = $hint =~ m/(.*?) \s*- \s*(.*)/;
    2.53                  next unless $command =~ s/\($entries_class\)//i;
    2.54 -                $new_commands_section .= "<tr><td valign='top'>$command</td><td>$hint</td></tr>"  if $hint;
    2.55 +                $new_commands_section .= "<tr><td valign='top'>$command</td><td>$hint</td></tr>";
    2.56          }
    2.57      }
    2.58      return $new_commands_section;
    2.59 @@ -761,9 +773,11 @@
    2.60  
    2.61      my $result;
    2.62      my ($command_lines,$toc)  = print_command_lines;
    2.63 +    my $files_section         = print_files;
    2.64  
    2.65      $result = print_header($toc);
    2.66      $result.= "<h2 id='log'>Журнал</h2>"       . $command_lines;
    2.67 +    $result.= "<h2 id='files'>Файлы</h2>"      . $files_section if $files_section;
    2.68      $result.= "<h2 id='stat'>Статистика</h2>"  . print_stat;
    2.69      $result.= "<h2 id='help'>Справка</h2>"     . $Html_Help . "<br/>"; 
    2.70      $result.= "<h2 id='about'>О программе</h2>". $Html_About. "<br/>"; 
    2.71 @@ -810,16 +824,20 @@
    2.72      }
    2.73  
    2.74      # Управляющая форма
    2.75 -    my $control_form .= "<table id='visibility_form' class='visibility_form'><tr><td>Видимые элементы</TD></tr><tr><td><form>\n";
    2.76 -    for my $element (keys %Elements_Visibility)
    2.77 +    my $control_form .= "<div class='visibility_form' title='Выберите какие элементы должны быть показаны в журнале'>"
    2.78 +                     .  "<span class='header'>Видимые элементы</span>"
    2.79 +                     .  "<span class='window_controls'><a href='' onclick='' title='свернуть форму управления'>_</a> <a href='' onclick='' title='закрыть форму управления'>x</a></span>"
    2.80 +                     .  "<div><form>\n";
    2.81 +    for my $element (sort keys %Elements_Visibility)
    2.82      {
    2.83 -        my @e = split /\s+/, $element;
    2.84 +        my ($skip, @e) = split /\s+/, $element;
    2.85          my $showhide = join "", map { "ShowHide('$_');" } @e ;
    2.86 -        $control_form .= "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
    2.87 +        $control_form .= "<div><input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
    2.88                  $Elements_Visibility{$element}.
    2.89 -                "</input><br>\n";
    2.90 +                "</input></div>";
    2.91      }
    2.92 -    $control_form .= "</form></td></tr></table>\n";
    2.93 +    $control_form .= "</form>\n"
    2.94 +                  .  "</div>\n";
    2.95  
    2.96      my $result;
    2.97      $result = <<HEADER;
    2.98 @@ -881,6 +899,7 @@
    2.99      <ul>
   2.100          <li><a href='#log'>Журнал</a></li>
   2.101          <ul>$toc</ul>
   2.102 +        <li><a href='#files'>Файлы</a></li>
   2.103          <li><a href='#stat'>Статистика</a></li>
   2.104          <li><a href='#help'>Справка</a></li>
   2.105          <li><a href='#about'>О программе</a></li>
   2.106 @@ -1028,6 +1047,19 @@
   2.107  }
   2.108  
   2.109  
   2.110 +sub print_files
   2.111 +{
   2.112 +    my $result = qq(); 
   2.113 +    my @toc;
   2.114 +    for my $file (sort keys %Files) {
   2.115 +          my $div_id = $file;
   2.116 +          $div_id =~ s@/@_@g;
   2.117 +          push @toc, "<a href='#$div_id'>$file</a>";
   2.118 +          $result .= "<div class='filename' id='$div_id'>".$file."</div>\n"
   2.119 +                  .  "<div class='filedata'><pre>".$Files{$file}."</pre></div>";
   2.120 +    }
   2.121 +    return "<div class='files_toc'>".collapse_list(\@toc)."</div>".$result;
   2.122 +}
   2.123  
   2.124  
   2.125  sub init_variables
   2.126 @@ -1053,9 +1085,9 @@
   2.127  <table>
   2.128  <tr class='command'>
   2.129  <td class='script'>
   2.130 -<pre class='mistyped_cline'>
   2.131 +<pre class='_mistyped_cline'>
   2.132  \$ l s-l</pre>
   2.133 -<pre class='mistyped_output'>bash: l: command not found
   2.134 +<pre class='_mistyped_output'>bash: l: command not found
   2.135  </pre>
   2.136  </td>
   2.137  </tr>
   2.138 @@ -1069,7 +1101,7 @@
   2.139  <table>
   2.140  <tr class='command'>
   2.141  <td class='script'>
   2.142 -<pre class='wrong_cline'>
   2.143 +<pre class='_wrong_cline'>
   2.144  \$ test 5 -lt 4</pre>
   2.145  </pre>
   2.146  </td>
   2.147 @@ -1085,7 +1117,7 @@
   2.148  <table>
   2.149  <tr class='command'>
   2.150  <td class='script'>
   2.151 -<pre class='interrupted_cline'>
   2.152 +<pre class='_interrupted_cline'>
   2.153  \$ find / -name abc</pre>
   2.154  <pre class='interrupted_output'>find: /home/devi-orig/.gnome2: Keine Berechtigung
   2.155  find: /home/devi-orig/.gnome2_private: Keine Berechtigung
   2.156 @@ -1266,7 +1298,7 @@
   2.157      <p>
   2.158      LiLaLo (L3) расшифровывается как Live Lab Log.<br/>
   2.159      Программа разработана для повышения эффективности обучения Unix/Linux-системам.<br/>
   2.160 -    (c) Игорь Чубин, 2004-2005<br/>
   2.161 +    (c) Игорь Чубин, 2004-2006<br/>
   2.162      </p>
   2.163  ABOUT
   2.164  $Html_About.='$Id$ </p>';
   2.165 @@ -1337,15 +1369,17 @@
   2.166      );
   2.167  
   2.168  %Elements_Visibility = (
   2.169 -        "note"      =>  "замечания",
   2.170 -        "diff"      =>  "редактор",
   2.171 -        "time"      =>  "время",
   2.172 -        "ttychange"     =>  "терминал",
   2.173 -        "wrong_output wrong_cline wrong_root_output wrong_root_cline" 
   2.174 -                =>  "команды с ошибками",
   2.175 -        "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" 
   2.176 +        "0 new_commands_table"      =>  "новые команды",
   2.177 +        "1 diff"      =>  "редактор",
   2.178 +        "2 time"      =>  "время",
   2.179 +        "3 ttychange"     =>  "терминал",
   2.180 +        "4 wrong_output wrong_cline wrong_root_output wrong_root_cline" 
   2.181 +                =>  "команды с ненулевым кодом завершения",
   2.182 +        "5 mistyped_output mistyped_cline mistyped_root_output mistyped_root_cline" 
   2.183 +                =>  "неверно набранные команды",
   2.184 +        "6 interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" 
   2.185                  =>  "прерванные команды",
   2.186 -        "tab_completion_output tab_completion_cline"    
   2.187 +        "7 tab_completion_output tab_completion_cline"    
   2.188                  =>  "продолжение с помощью tab"
   2.189  );
   2.190  
     3.1 --- a/l3config.pm	Sat Jan 28 09:00:25 2006 +0200
     3.2 +++ b/l3config.pm	Tue Jan 31 20:18:54 2006 +0200
     3.3 @@ -14,122 +14,123 @@
     3.4  
     3.5  
     3.6  our %Config = (
     3.7 -	"skip_empty"		=> "yes",
     3.8 -	"skip_interrupted"	=> "no",
     3.9 -	"skip_wrong" 		=> "no",
    3.10 -	"editors"		    => ["vi", "pico", "ee", "vim", "nano"],
    3.11 -	"pagers"		    => ["more", "less", "zmore", "zless", "info", 
    3.12 -				        "man", "mc", "trafshow", "screen", "cfdisk",
    3.13 -				        "trafshow-bsd", "yes", "lynx", "links", "centericq"
    3.14 -				        ],
    3.15 -	"terminal"		    => ["mc"],
    3.16 -	"suppress_editors"	=> "yes",
    3.17 -	"suppress_pagers"	=> "yes",
    3.18 -	"suppress_terminal"	=> "yes",
    3.19 +    "skip_empty"        => "yes",
    3.20 +    "skip_interrupted"  => "no",
    3.21 +    "skip_wrong"        => "no",
    3.22 +    "editors"           => ["vi", "pico", "ee", "vim", "nano"],
    3.23 +    "pagers"            => ["more", "less", "zmore", "zless", "info", 
    3.24 +                            "man", "mc", "trafshow", "screen", "cfdisk",
    3.25 +                            "trafshow-bsd", "yes", "lynx", "links", "centericq"
    3.26 +                            ],
    3.27 +    "full_output_commands" => ["cat"],
    3.28 +    "terminal"          => ["mc"],
    3.29 +    "suppress_editors"  => "yes",
    3.30 +    "suppress_pagers"   => "yes",
    3.31 +    "suppress_terminal" => "yes",
    3.32  
    3.33 -	"terminal_width"	=> 400,
    3.34 -	"terminal_height"	=> 100,
    3.35 -	"verbose"		    => "yes",
    3.36 +    "terminal_width"    => 400,
    3.37 +    "terminal_height"   => 100,
    3.38 +    "verbose"           => "yes",
    3.39  
    3.40 -	"head_lines"		=> 5,
    3.41 -	"tail_lines"		=> 5,
    3.42 -	"cache_head_lines"	=> 50,
    3.43 -	"cache_tail_lines"	=> 50,
    3.44 -	"skip_text"		    => "...",
    3.45 -	"show_time"		    => "yes",
    3.46 -	"show_diffs"		=> "yes",
    3.47 -	"show_comments"		=> "yes",
    3.48 -	"show_notes"		=> "yes",
    3.49 +    "head_lines"        => 5,
    3.50 +    "tail_lines"        => 5,
    3.51 +    "cache_head_lines"  => 50,
    3.52 +    "cache_tail_lines"  => 50,
    3.53 +    "skip_text"         => "...",
    3.54 +    "show_time"         => "yes",
    3.55 +    "show_diffs"        => "yes",
    3.56 +    "show_comments"     => "yes",
    3.57 +    "show_notes"        => "yes",
    3.58  
    3.59 -	"input"			=> "$ENV{HOME}/.lilalo",
    3.60 -	"diffs"			=> "",
    3.61 -	"input_mask"		=> "*.script",
    3.62 -	"encoding"		=> "utf-8",
    3.63 +    "input"             => "$ENV{HOME}/.lilalo",
    3.64 +    "diffs"             => "",
    3.65 +    "input_mask"        => "*.script",
    3.66 +    "encoding"          => "utf-8",
    3.67  
    3.68 -	"cache"			=> "$ENV{HOME}/.lilalo/report.xml",
    3.69 -	"cache_stat"		=> "$ENV{HOME}/.lilalo/.report.dat",
    3.70 +    "cache"             => "$ENV{HOME}/.lilalo/report.xml",
    3.71 +    "cache_stat"        => "$ENV{HOME}/.lilalo/.report.dat",
    3.72  
    3.73 -	"output"		=> "/tmp/report.html",
    3.74 -	"output_mask"		=> "INDEX",
    3.75 -	"output_format"		=> "html",
    3.76 -	"frontend_css"		=> "/l3/l3.css",
    3.77 -	"frontend_google_ico"	=> "/l3/google.ico",
    3.78 -	"frontend_linux_ico"	=> "/l3/linux.ico",
    3.79 -	"frontend_freebsd_ico"	=> "/l3/freebsd.ico",
    3.80 -	"frontend_opennet_ico"	=> "/l3/opennet.ico",
    3.81 -	"frontend_local_ico"	=> "/l3/freebsd.ico",
    3.82 +    "output"            => "/tmp/report.html",
    3.83 +    "output_mask"       => "INDEX",
    3.84 +    "output_format"     => "html",
    3.85 +    "frontend_css"      => "/l3/l3.css",
    3.86 +    "frontend_google_ico"   => "/l3/google.ico",
    3.87 +    "frontend_linux_ico"    => "/l3/linux.ico",
    3.88 +    "frontend_freebsd_ico"  => "/l3/freebsd.ico",
    3.89 +    "frontend_opennet_ico"  => "/l3/opennet.ico",
    3.90 +    "frontend_local_ico"    => "/l3/freebsd.ico",
    3.91  
    3.92 -	"mywi_server"		=> "127.0.0.1",
    3.93 -	"mywi_port"		=> "19801",
    3.94 +    "mywi_server"       => "127.0.0.1",
    3.95 +    "mywi_port"         => "19801",
    3.96  
    3.97 -	"stat_inactivity_interval"	=>	"1800",
    3.98 +    "stat_inactivity_interval"  =>  "1800",
    3.99  
   3.100 -	"signature"		=> "#lm:",
   3.101 -	"from"			=> "",
   3.102 -	"to"			=> "",
   3.103 -	"lab"			=> "",
   3.104 -	"keywords"		=> "linux command",
   3.105 -	"files_keywords"	=> "linux file",
   3.106 +    "signature"         => "#lm:",
   3.107 +    "from"              => "",
   3.108 +    "to"                => "",
   3.109 +    "lab"               => "",
   3.110 +    "keywords"          => "linux command",
   3.111 +    "files_keywords"    => "linux file",
   3.112  
   3.113 -	comment_width		=> "300",
   3.114 -	note_width		=> "500",
   3.115 -	time_width		=> "60",
   3.116 +    comment_width       => "300",
   3.117 +    note_width      => "500",
   3.118 +    time_width      => "60",
   3.119  
   3.120 -	"mode"			=> "daemon",		# daemon | normal
   3.121 -	"daemon_sleep_interval"	=> "10",
   3.122 -	"detach"		=> "yes",
   3.123 -	"agent_pidfile"		=> "$ENV{HOME}/.lilalo/l3-agent.pid",
   3.124 +    "mode"          => "daemon",        # daemon | normal
   3.125 +    "daemon_sleep_interval" => "10",
   3.126 +    "detach"        => "yes",
   3.127 +    "agent_pidfile"     => "$ENV{HOME}/.lilalo/l3-agent.pid",
   3.128  
   3.129 -	"backend_address"	=> "192.168.15.254",
   3.130 -	"backend_port"		=> "18030",
   3.131 -	"backend_pidfile"	=> "/tmp/l3-backend.pid",
   3.132 -	"backend_datafile"	=> "/var/lilalo/lablogs-xml/backend.xml",
   3.133 +    "backend_address"   => "192.168.15.254",
   3.134 +    "backend_port"      => "18030",
   3.135 +    "backend_pidfile"   => "/tmp/l3-backend.pid",
   3.136 +    "backend_datafile"  => "/var/lilalo/lablogs-xml/backend.xml",
   3.137  
   3.138 -	"l3-agent"		=> "l3-agent", 
   3.139 -	"l3-backend"		=> "l3-backend", 
   3.140 +    "l3-agent"      => "l3-agent", 
   3.141 +    "l3-backend"        => "l3-backend", 
   3.142  
   3.143 -	"course-name" 		=> "", 
   3.144 -	"course-code" 		=> "", 
   3.145 -	"course-date" 		=> "", 
   3.146 -	"course-center" 	=> "", 
   3.147 -	"course-trainer" 	=> "", 
   3.148 -	"course-student" 	=> "", 
   3.149 +    "course-name"       => "", 
   3.150 +    "course-code"       => "", 
   3.151 +    "course-date"       => "", 
   3.152 +    "course-center"     => "", 
   3.153 +    "course-trainer"    => "", 
   3.154 +    "course-student"    => "", 
   3.155  
   3.156 -	"filter" 		=> "",
   3.157 +    "filter"        => "",
   3.158  #lm
   3.159 -	"show_host" 		=> "no",
   3.160 +    "show_host"         => "no",
   3.161  
   3.162  # Вспомогательные программы
   3.163 -#"l3-report"	=> "./lm-report",
   3.164 -	"l3-report"		=> "./l3-report",
   3.165 +#"l3-report"    => "./lm-report",
   3.166 +    "l3-report"     => "./l3-report",
   3.167  
   3.168  # Каталоги
   3.169 -	"path_lilalo" 		=> "/var/lilalo/",
   3.170 -	"path_classes"		=> "/var/lilalo/classes/",
   3.171 -	"path_lablogs"		=> "/var/lilalo/lablogs/",
   3.172 -	"courses_path"		=> "/var/lilalo/courses/",
   3.173 -	"outpath"		=> "/var/lilalo/out/",
   3.174 -	"path_web"		=> "/var/www/l3",	# Путь к web-отчётам
   3.175 -	"path_share"		=> "./share/",		# Путь к web-отчётам
   3.176 +    "path_lilalo"       => "/var/lilalo/",
   3.177 +    "path_classes"      => "/var/lilalo/classes/",
   3.178 +    "path_lablogs"      => "/var/lilalo/lablogs/",
   3.179 +    "courses_path"      => "/var/lilalo/courses/",
   3.180 +    "outpath"       => "/var/lilalo/out/",
   3.181 +    "path_web"      => "/var/www/l3",   # Путь к web-отчётам
   3.182 +    "path_share"        => "./share/",      # Путь к web-отчётам
   3.183  
   3.184  # Файлы
   3.185 -	"runfile"		=> "lm.run", 
   3.186 -	"logfile"		=> "lm.log", 
   3.187 +    "runfile"       => "lm.run", 
   3.188 +    "logfile"       => "lm.log", 
   3.189  
   3.190 -	"class" 		=> "class", 		# Имя файла класса
   3.191 -	"class_suffix" 		=> ".xml", 		# Cуффикс файла класса
   3.192 -	"classfile"		=> "",
   3.193 +    "class"         => "class",         # Имя файла класса
   3.194 +    "class_suffix"      => ".xml",      # Cуффикс файла класса
   3.195 +    "classfile"     => "",
   3.196  
   3.197 -	"sshkey"		=> "$ENV{HOME}/.ssh/id_dsa.pub",
   3.198 -	"lmssh"			=> "./lm-ssh",
   3.199 -	"lminstall"		=> "./lm-install",
   3.200 -	"ssh_user"		=> "root",
   3.201 +    "sshkey"        => "$ENV{HOME}/.ssh/id_dsa.pub",
   3.202 +    "lmssh"         => "./lm-ssh",
   3.203 +    "lminstall"     => "./lm-install",
   3.204 +    "ssh_user"      => "root",
   3.205  
   3.206 -	"l3scripts"		=> "l3scripts",
   3.207 +    "l3scripts"     => "l3scripts",
   3.208  
   3.209  
   3.210 -	"cgi_path_info" 	=> "",
   3.211 -	"cgi2file"		=> "",
   3.212 +    "cgi_path_info"     => "",
   3.213 +    "cgi2file"      => "",
   3.214  
   3.215      "year"      =>  "2006",
   3.216  );
     4.1 --- a/lm	Sat Jan 28 09:00:25 2006 +0200
     4.2 +++ b/lm	Tue Jan 31 20:18:54 2006 +0200
     4.3 @@ -68,6 +68,7 @@
     4.4  sub lm_do;
     4.5  sub lm_report;
     4.6  sub lm_show_hosts;
     4.7 +sub lm_show_email;
     4.8  sub lm_show_labs;
     4.9  
    4.10  sub load_run;
    4.11 @@ -119,6 +120,7 @@
    4.12  		case "start"	{ lm_start }
    4.13  		case "stop"	{ lm_stop }
    4.14  		case "show hosts" { lm_show_hosts }
    4.15 +		case "show email" { lm_show_email }
    4.16  		case "show labs" { lm_show_labs }
    4.17  		case /do /  { $arg =~ /do (.*)/;  lm_do "$1" }
    4.18  		else 		{ print_usage_info() }
    4.19 @@ -173,6 +175,7 @@
    4.20  			"name"	=> "$student->{firstname} $student->{surname}",
    4.21  			"firstname"	=> "$student->{firstname}",
    4.22  			"user"	=> "$student->{user}",
    4.23 +			"email"	=> "$student->{email}",
    4.24  			"student" => $student,
    4.25  		}	
    4.26  	}
    4.27 @@ -258,6 +261,18 @@
    4.28  	}
    4.29  }
    4.30  
    4.31 +sub lm_show_email
    4.32 +# Show hosts used to run a commands
    4.33 +{
    4.34 +	my $i=1;
    4.35 +	for my $m (sort keys %Machines) {
    4.36 +		if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) {
    4.37 +			print $Machines{$m}->{"email"},"\t",$Machines{$m}->{"name"},"\n";
    4.38 +		}	
    4.39 +		$i++;
    4.40 +	}
    4.41 +}
    4.42 +
    4.43  sub lm_show_labs
    4.44  # Show hosts used to run a commands
    4.45  {