]> git.proxmox.com Git - pve-common.git/blobdiff - data/PVE/Tools.pm
add split_args helper function
[pve-common.git] / data / PVE / Tools.pm
index 985b31ef4ec4a1523cda4e669846a719068cb0f1..5cd8460db707bbef381a02611afa188ba982a6a0 100644 (file)
@@ -177,13 +177,11 @@ sub run_command {
     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') {
@@ -198,15 +196,23 @@ sub run_command {
                };
            } 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'; 
  
@@ -219,6 +225,15 @@ sub run_command {
            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 = $@;
@@ -235,11 +250,13 @@ sub run_command {
        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 = '';
@@ -261,12 +278,13 @@ sub run_command {
                }
                $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 = $@;
@@ -280,12 +298,13 @@ sub run_command {
                        *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 = $@;
@@ -303,7 +322,10 @@ sub run_command {
        }
 
        &$outfunc($outlog) if $outfunc && $outlog;
+       &$logfunc($outlog) if $logfunc && $outlog;
+
        &$errfunc($errlog) if $errfunc && $errlog;
+       &$logfunc($errlog) if $logfunc && $errlog;
 
        waitpid ($pid, 0);
   
@@ -312,12 +334,14 @@ sub run_command {
        } 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);
@@ -346,6 +370,8 @@ sub run_command {
            die "command '$cmdstr' failed: $err";
        }
     }
+
+    return undef;
 }
 
 sub split_list {
@@ -661,4 +687,52 @@ sub random_ether_addr {
     return $mac;
 }
 
+sub shellquote {
+    my $str = shift;
+
+    return "''" if !defined ($str) || ($str eq '');
+    
+    die "unable to quote string containing null (\\000) bytes"
+       if $str =~ m/\x00/;
+
+    # from String::ShellQuote
+    if ($str =~ m|[^\w!%+,\-./:@^]|) {
+
+       # ' -> '\''
+       $str =~ s/'/'\\''/g;
+
+       $str = "'$str'";
+       $str =~ s/^''//;
+       $str =~ s/''$//;
+    }
+
+    return $str;
+}
+
+# a clumsy way to split an shell argument string into an array,
+# we simply pass it to the cli (exec call)
+# fixme: use Text::ParseWords::shellwords() ?
+sub split_args {
+    my ($str) = @_;
+
+    my $args = [];
+
+    return $args if !$str;
+
+    my $cmd = 'perl -e \'foreach my $a (@ARGV) { print "$a\n"; } \' -- ' . $str;
+
+    eval {
+       run_command($cmd, outfunc => sub {
+           my $data = shift;
+           push @$args, $data;
+       });
+    };
+
+    my $err = $@;
+
+    die "unable to parse args: $str\n" if $err;
+
+    return $args;
+}
+
 1;