]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QMPClient.pm
return error from guest-agent
[qemu-server.git] / PVE / QMPClient.pm
index b4d56125df19aa82410ac37dd21ce1dbaeec1cfa..2277fef398b2df6f73f5aa6fb3932e9493fbf98b 100755 (executable)
@@ -92,6 +92,7 @@ sub cmd {
     my $callback = sub {
        my ($vmid, $resp) = @_;
        $result = $resp->{'return'};
+       $result = { error => $resp->{'error'} } if !defined($result) && $resp->{'error'};
     };
 
     die "no command specified" if !($cmd && $cmd->{execute});
@@ -107,10 +108,20 @@ sub cmd {
            $timeout = 60*60; # 1 hour
        } elsif ($cmd->{execute} =~ m/^(eject|change)/) {
            $timeout = 60; # note: cdrom mount command is slow
+       } elsif ($cmd->{execute} eq 'guest-fsfreeze-freeze') {
+           # freeze syncs all guest FS, if we kill it it stays in an unfreezable
+           # locked state with high probability, so use an generous timeout
+           $timeout = 60*60; # 1 hour
+       } elsif ($cmd->{execute} eq 'guest-fsfreeze-thaw') {
+           # thaw has no possible long blocking actions, either it returns
+           # instantly or never (dead locked)
+           $timeout = 10;
        } elsif ($cmd->{execute} eq 'savevm-start' ||
                 $cmd->{execute} eq 'savevm-end' ||
                 $cmd->{execute} eq 'query-backup' ||
                 $cmd->{execute} eq 'query-block-jobs' ||
+                $cmd->{execute} eq 'block-job-cancel' ||
+                $cmd->{execute} eq 'block-job-complete' ||
                 $cmd->{execute} eq 'backup-cancel' ||
                 $cmd->{execute} eq 'query-savevm' ||
                 $cmd->{execute} eq 'delete-drive-snapshot' || 
@@ -122,8 +133,7 @@ sub cmd {
        }
     }
 
-    $self->queue_execute($timeout);
-
+    $self->queue_execute($timeout, 2);
 
     die "VM $vmid qmp command '$cmd->{execute}' failed - $queue_info->{error}"
        if defined($queue_info->{error});
@@ -162,7 +172,7 @@ my $close_connection = sub {
 
     if (my $fh = delete $queue_info->{fh}) {
        delete $self->{queue_lookup}->{$fh};
-       $self->{mux}->close($fh);       
+       $self->{mux}->close($fh);
     } 
 };
 
@@ -250,7 +260,8 @@ my $check_queue = sub {
 
            if ($qga) {
 
-               $qmpcmd = to_json({ execute => 'guest-sync', arguments => { id => int($cmd->{id})}}) .
+               $qmpcmd = to_json({ execute => 'guest-sync-delimited', 
+                                   arguments => { id => int($cmd->{id})}}) .
                    to_json({ execute => $cmd->{execute}, arguments => $cmd->{arguments}});
 
            } else {
@@ -281,8 +292,9 @@ my $check_queue = sub {
 };
 
 # execute all queued command
+
 sub queue_execute {
-    my ($self, $timeout) = @_;
+    my ($self, $timeout, $noerr) = @_;
 
     $timeout = 3 if !$timeout;
 
@@ -303,7 +315,6 @@ sub queue_execute {
            }
        };
        if (my $err = $@) {
-           warn $err;
            $queue_info->{error} = $err;
        }
     }
@@ -320,11 +331,22 @@ sub queue_execute {
     }
 
     # make sure we close everything
+    my $errors = '';
     foreach my $sname (keys %{$self->{queue_info}}) {
-       &$close_connection($self, $self->{queue_info}->{$sname});
+       my $queue_info = $self->{queue_info}->{$sname};
+       &$close_connection($self, $queue_info);
+       if ($queue_info->{error}) {
+           if ($noerr) {
+               warn $queue_info->{error} if $noerr < 2;
+           } else {
+               $errors .= $queue_info->{error}
+           }
+       }
     }
 
     $self->{queue_info} = $self->{queue_lookup} = {};
+
+    die $errors if $errors;
 }
 
 sub mux_close {
@@ -337,8 +359,7 @@ sub mux_close {
        if !$queue_info->{error};
 }
 
-# mux_input is called when input is available on one of
-# the descriptors.
+# mux_input is called when input is available on one of the descriptors.
 sub mux_input {
     my ($self, $mux, $fh, $input) = @_;
 
@@ -355,10 +376,10 @@ sub mux_input {
     my $raw;
 
     if ($qga) {
-       return if $$input !~ s/^([^\n]+}\n[^\n]+})\n(.*)$/$2/so;
+       return if $$input !~ s/^.*\xff([^\n]+}\r?\n[^\n]+})\r?\n(.*)$/$2/so;
        $raw = $1;
     } else {
-       return if $$input !~ s/^([^\n]+})\r?\n(.*)$/$2/so;
+       return if $$input !~ s/^(.*})\r?\n(.*)$/$2/so;
        $raw = $1;
     }
 
@@ -370,15 +391,19 @@ sub mux_input {
            die "response is not complete" if @jsons != 2 ;
 
            my $obj = from_json($jsons[0]);
+
            my $cmdid = $obj->{'return'};
            die "received responsed without command id\n" if !$cmdid;
-           
-           delete $queue_info->{current};
 
+           # skip results fro previous commands
+           return if $cmdid < $curcmd->{id};
+           
            if ($curcmd->{id} ne $cmdid) {
                die "got wrong command id '$cmdid' (expected $curcmd->{id})\n";
            }
 
+           delete $queue_info->{current};
+
            $obj = from_json($jsons[1]);
 
            if (my $callback = $curcmd->{callback}) {
@@ -409,12 +434,12 @@ sub mux_input {
            my $cmdid = $obj->{id};
            die "received responsed without command id\n" if !$cmdid;
 
-           delete $queue_info->{current};
-
            if ($curcmd->{id} ne $cmdid) {
                die "got wrong command id '$cmdid' (expected $curcmd->{id})\n";
            }
 
+           delete $queue_info->{current};
+
            if (my $callback = $curcmd->{callback}) {
                &$callback($vmid, $obj);
            }
@@ -433,6 +458,7 @@ sub mux_timeout {
 
     if (my $queue_info = &$lookup_queue_info($self, $fh)) { 
        $queue_info->{error} = "got timeout\n";
+       $self->{mux}->inbuffer($fh, ''); # clear to avoid warnings
     }
 
     &$check_queue($self);
@@ -440,7 +466,7 @@ sub mux_timeout {
 
 sub mux_eof {
     my ($self, $mux, $fh, $input) = @_;
+
     my $queue_info = &$lookup_queue_info($self, $fh);
     return if !$queue_info;
 
@@ -453,18 +479,31 @@ sub mux_eof {
 
     if ($qga && $qga_allow_close_cmds->{$curcmd->{execute}}) {
 
-       return if $$input !~ s/^([^\n]+})\n(.*)$/$2/so;
+       return if $$input !~ s/^.*\xff([^\n]+})\r?\n(.*)$/$2/so;
 
-       my @jsons = split("\n", $1);
+       my $raw = $1;
 
-       my $obj = from_json($jsons[0]);
+       eval {
+           my $obj = from_json($raw);
 
-       my $cmdid = $obj->{'return'};
-       die "received responsed without command id\n" if !$cmdid;
+           my $cmdid = $obj->{'return'};
+           die "received responsed without command id\n" if !$cmdid;
 
-       delete $queue_info->{current};
+           delete $queue_info->{current};
+
+           if (my $callback = $curcmd->{callback}) {
+               &$callback($vmid, undef);
+           }
+       };
+       if (my $err = $@) {
+           $queue_info->{error} = $err;
+       }
 
        &$close_connection($self, $queue_info);
+
+       if (scalar(@{$queue_info->{cmds}}) && !$queue_info->{error}) {
+           $queue_info->{error} = "Got EOF but command queue is not empty.\n";
+       }
     }
 }