+sub random_ether_addr {
+
+ my $rand = Digest::SHA1::sha1_hex(rand(), time());
+
+ my $mac = '';
+ for (my $i = 0; $i < 6; $i++) {
+ my $ss = hex(substr($rand, $i*2, 2));
+ if (!$i) {
+ $ss &= 0xfe; # clear multicast
+ $ss |= 2; # set local id
+ }
+ $ss = sprintf("%02X", $ss);
+
+ if (!$i) {
+ $mac .= "$ss";
+ } else {
+ $mac .= ":$ss";
+ }
+ }
+
+ return $mac;
+}
+
+sub shellquote {
+ my $str = shift;
+
+ return String::ShellQuote::shell_quote($str);
+}
+
+# split an shell argument string into an array,
+sub split_args {
+ my ($str) = @_;
+
+ return $str ? [ Text::ParseWords::shellwords($str) ] : [];
+}
+
+sub dump_logfile {
+ my ($filename, $start, $limit) = @_;
+
+ my $lines = [];
+ my $count = 0;
+
+ my $fh = IO::File->new($filename, "r");
+ if (!$fh) {
+ $count++;
+ push @$lines, { n => $count, t => "unable to open file - $!"};
+ return ($count, $lines);
+ }
+
+ $start = 0 if !$start;
+ $limit = 50 if !$limit;
+
+ my $line;
+ while (defined($line = <$fh>)) {
+ next if $count++ < $start;
+ next if $limit <= 0;
+ chomp $line;
+ push @$lines, { n => $count, t => $line};
+ $limit--;
+ }
+
+ close($fh);
+
+ # HACK: ExtJS store.guaranteeRange() does not like empty array
+ # so we add a line
+ if (!$count) {
+ $count++;
+ push @$lines, { n => $count, t => "no content"};
+ }
+
+ return ($count, $lines);
+}