use base 'Exporter';
use URI::Escape;
use Encode;
+use Digest::SHA1;
+use Text::ParseWords;
+use String::ShellQuote;
our @EXPORT_OK = qw(
lock_file
my $pid;
eval {
- my $reader = IO::File->new();
- my $writer = IO::File->new();
- my $error = IO::File->new();
-
my $input;
+ my $output;
my $outfunc;
my $errfunc;
+ my $logfunc;
foreach my $p (keys %param) {
if ($p eq 'timeout') {
};
} elsif ($p eq 'input') {
$input = $param{$p};
+ } elsif ($p eq 'output') {
+ $output = $param{$p};
} elsif ($p eq 'outfunc') {
$outfunc = $param{$p};
} elsif ($p eq 'errfunc') {
$errfunc = $param{$p};
+ } elsif ($p eq 'logfunc') {
+ $logfunc = $param{$p};
} else {
die "got unknown parameter '$p' for run_command\n";
}
}
+ my $reader = $output && $output =~ m/^>&/ ? $output : IO::File->new();
+ my $writer = $input && $input =~ m/^<&/ ? $input : IO::File->new();
+ my $error = IO::File->new();
+
# try to avoid locale related issues/warnings
my $lang = $param{lang} || 'C';
local $ENV{LVM_SUPPRESS_FD_WARNINGS} = "1";
$pid = open3($writer, $reader, $error, @$cmd) || die $!;
+
+ # if we pipe fron STDIN, open3 closes STDIN, so we we
+ # a perl warning "Filehandle STDIN reopened as GENXYZ .. "
+ # as soon as we open a new file.
+ # to avoid that we open /dev/null
+ if (!ref($writer) && !defined(fileno(STDIN))) {
+ POSIX::close(0);
+ open(STDIN, "</dev/null");
+ }
};
my $err = $@;
local $SIG{ALRM} = sub { die "got timeout\n"; } if $timeout;
$oldtimeout = alarm($timeout) if $timeout;
- print $writer $input if defined $input;
- close $writer;
+ if (ref($writer)) {
+ print $writer $input if defined $input;
+ close $writer;
+ }
my $select = new IO::Select;
- $select->add($reader);
+ $select->add($reader) if ref($reader);
$select->add($error);
my $outlog = '';
}
$select->remove ($h) if !$count;
if ($h eq $reader) {
- if ($outfunc) {
+ if ($outfunc || $logfunc) {
eval {
$outlog .= $buf;
while ($outlog =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) {
my $line = $1;
- &$outfunc($line);
+ &$outfunc($line) if $outfunc;
+ &$logfunc($line) if $logfunc;
}
};
my $err = $@;
*STDOUT->flush();
}
} elsif ($h eq $error) {
- if ($errfunc) {
+ if ($errfunc || $logfunc) {
eval {
$errlog .= $buf;
while ($errlog =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) {
my $line = $1;
- &$errfunc($line);
+ &$errfunc($line) if $errfunc;
+ &$logfunc($line) if $logfunc;
}
};
my $err = $@;
}
&$outfunc($outlog) if $outfunc && $outlog;
+ &$logfunc($outlog) if $logfunc && $outlog;
+
&$errfunc($errlog) if $errfunc && $errlog;
+ &$logfunc($errlog) if $logfunc && $errlog;
waitpid ($pid, 0);
} elsif (my $sig = ($? & 127)) {
die "got signal $sig\n";
} elsif (my $ec = ($? >> 8)) {
- if ($errmsg && $laststderr) {
- my $lerr = $laststderr;
- $laststderr = undef;
- die "$lerr\n";
+ if (!($ec == 24 && ($cmdstr =~ m|^(\S+/)?rsync\s|))) {
+ if ($errmsg && $laststderr) {
+ my $lerr = $laststderr;
+ $laststderr = undef;
+ die "$lerr\n";
+ }
+ die "exit code $ec\n";
}
- die "exit code $ec\n";
}
alarm(0);
die "command '$cmdstr' failed: $err";
}
}
+
+ return undef;
}
sub split_list {
my $listtxt = shift || '';
- $listtxt =~ s/[,;\0]/ /g;
+ return split (/\0/, $listtxt) if $listtxt =~ m/\0/;
+
+ $listtxt =~ s/[,;]/ /g;
$listtxt =~ s/^\s+//;
my @data = split (/\s+/, $listtxt);
my $outfh = IO::File->new ($filename, O_WRONLY|O_CREAT|O_EXCL, $perm) ||
die "unable to create output file '$filename' - $!\n";
- chown $wwwid, $outfh;
+ chown $wwwid, -1, $outfh;
return $outfh;
};
return Encode::decode("utf8", uri_unescape($data));
}
+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);
+}
1;