]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
new vga_conf_has_spice() helper
[qemu-server.git] / PVE / QemuServer.pm
index 62c74cc61136812a53ee420076a138f32d45ad3e..c5bd44433df0438ce3df893375d175b286523352 100644 (file)
@@ -377,7 +377,7 @@ EODESCR
        optional => 1,
        type => 'boolean',
        default => 1,
-       description => "Enable/disable the usb tablet device. This device is usually needed to allow absolute mouse positioning. Else the mouse runs out of sync with normal vnc clients. If you're running lots of console-only guests on one host, you may consider disabling this to save some context switches.",
+       description => "Enable/disable the usb tablet device. This device is usually needed to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with normal VNC clients. If you're running lots of console-only guests on one host, you may consider disabling this to save some context switches. This is turned of by default if you use spice (vga=qxl).",
     },
     migrate_speed => {
        optional => 1,
@@ -535,7 +535,7 @@ PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
 my $usbdesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-usb-device',
-    typetext => 'host=HOSTUSBDEVICE',
+    typetext => 'host=HOSTUSBDEVICE|spice',
     description => <<EODESCR,
 Configure an USB device (n is 0 to 4). This can be used to
 pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
@@ -547,6 +547,8 @@ You can use the 'lsusb -t' command to list existing usb devices.
 
 Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
+The value 'spice' can be used to add a usb redirection devices for spice.
+
 EODESCR
 };
 PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
@@ -925,7 +927,7 @@ sub parse_drive {
     return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/;
     return undef if $res->{aio} && $res->{aio} !~ m/^(native|threads)$/;
 
-    
+
     return undef if $res->{mbps_rd} && $res->{mbps};
     return undef if $res->{mbps_wr} && $res->{mbps};
 
@@ -941,7 +943,7 @@ sub parse_drive {
 
 
     if ($res->{size}) {
-       return undef if !defined($res->{size} = &$parse_size($res->{size})); 
+       return undef if !defined($res->{size} = &$parse_size($res->{size}));
     }
 
     if ($res->{media} && ($res->{media} eq 'cdrom')) {
@@ -995,7 +997,7 @@ sub scsi_inquiry {
 
     my $buf = "\x00" x 36;
     my $sensebuf = "\x00" x 8;
-    my $cmd = pack("C x3 C x11", 0x12, 36);
+    my $cmd = pack("C x3 C x1", 0x12, 36);
 
     # see /usr/include/scsi/sg.h
     my $sg_io_hdr_t = "i i C C s I P P P I I i P C C C C S S i I I";
@@ -1017,9 +1019,12 @@ sub scsi_inquiry {
     }
 
     my $res = {};
-    ($res->{device}, $res->{removable}, $res->{venodor},
+    (my $byte0, my $byte1, $res->{vendor},
      $res->{product}, $res->{revision}) = unpack("C C x6 A8 A16 A4", $buf);
 
+    $res->{removable} = $byte1 & 128 ? 1 : 0;
+    $res->{type} = $byte0 & 31;
+
     return $res;
 }
 
@@ -1050,7 +1055,7 @@ sub print_drivedevice_full {
         my $path = '';
         if (drive_is_cdrom($drive)) {
               $devicetype = 'cd';
-          } else {
+       } else {
               if ($drive->{file} =~ m|^/|) {
                   $path = $drive->{file};
               } else {
@@ -1058,10 +1063,15 @@ sub print_drivedevice_full {
               }
 
              if($path =~ m/^iscsi\:\/\//){
-                $devicetype = 'generic';
-             }
-             else {
-                $devicetype = 'block' if path_is_scsi($path);
+                 $devicetype = 'generic';
+             } else {
+                 if (my $info = path_is_scsi($path)) {
+                     if ($info->{type} == 0) {
+                         $devicetype = 'block';
+                     } elsif ($info->{type} == 1) { # tape
+                         $devicetype = 'generic';
+                     }
+                 }
              }
          }
 
@@ -1403,6 +1413,9 @@ sub parse_usb_device {
            $found = 1;
            $res->{hostbus} = $1;
            $res->{hostport} = $2;
+       } elsif ($v =~ m/^spice$/) {
+           $found = 1;
+           $res->{spice} = 1;
        } else {
            return undef;
        }
@@ -1624,12 +1637,12 @@ sub parse_vm_config {
     my @lines = split(/\n/, $raw);
     foreach my $line (@lines) {
        next if $line =~ m/^\s*$/;
-       
+
        if ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
            my $snapname = $1;
            $conf->{description} = $descr if $descr;
            $descr = '';
-           $conf = $res->{snapshots}->{$snapname} = {}; 
+           $conf = $res->{snapshots}->{$snapname} = {};
            next;
        }
 
@@ -1734,7 +1747,7 @@ sub write_vm_config {
            delete $conf->{$key};
        }
     }
-  
+
     my $generate_raw_config = sub {
        my ($conf) = @_;
 
@@ -1854,7 +1867,7 @@ sub shared_nodes {
     my $nodelist = PVE::Cluster::get_nodelist();
     my $nodehash = { map { $_ => 1 } @$nodelist };
     my $nodename = PVE::INotify::nodename();
-  
+
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
 
@@ -2113,13 +2126,13 @@ sub vmstatus {
 
        my $info = $resp->{'return'};
        return if !$info->{max_mem};
-       
+
        my $d = $res->{$vmid};
 
        # use memory assigned to VM
        $d->{maxmem} = $info->{max_mem};
        $d->{balloon} = $info->{actual};
-       
+
        if (defined($info->{total_mem}) && defined($info->{free_mem})) {
            $d->{mem} = $info->{total_mem} - $info->{free_mem};
            $d->{freemem} = $info->{free_mem};
@@ -2188,14 +2201,14 @@ sub foreach_drive {
 
 sub foreach_volid {
     my ($conf, $func) = @_;
-    
+
     my $volhash = {};
 
     my $test_volid = sub {
        my ($volid, $is_cdrom) = @_;
 
        return if !$volid;
-       
+
        $volhash->{$volid} = $is_cdrom || 0;
     };
 
@@ -2214,10 +2227,16 @@ sub foreach_volid {
     }
 
     foreach my $volid (keys %$volhash) {
-       &$func($volid, $volhash->{$volid});     
+       &$func($volid, $volhash->{$volid});
     }
 }
 
+sub vga_conf_has_spice {
+    my ($vga) = @_;
+
+    return $vga && ($vga eq 'qxl');
+}
+
 sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
 
@@ -2269,8 +2288,26 @@ sub config_to_command {
     # include usb device config
     push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
 
+    my $vga = $conf->{vga};
+    if (!$vga) {
+       if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || 
+                               $conf->{ostype} eq 'win7' || 
+                               $conf->{ostype} eq 'w2k8')) {
+           $vga = 'std';
+       } else {
+           $vga = 'cirrus';
+       }
+    }
+
     # enable absolute mouse coordinates (needed by vnc)
-    my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
+    my $tablet;
+    if (defined($conf->{tablet})) {
+       $tablet = $conf->{tablet};
+    } else {
+       $tablet = $defaults->{tablet};
+       $tablet = 0 if vga_conf_has_spice($vga); # disable for spice because it is not needed
+    }
+
     push @$devices, '-device', 'usb-tablet,id=tablet,bus=uhci.0,port=1' if $tablet;
 
     # host pci devices
@@ -2289,6 +2326,10 @@ sub config_to_command {
            push @$devices, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
        } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
            push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
+       } elsif ($d->{spice}) {
+           # usb redir support for spice
+           push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
+           push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
        }
     }
 
@@ -2338,15 +2379,6 @@ sub config_to_command {
 
     push @$cmd, '-no-reboot' if  defined($conf->{reboot}) && $conf->{reboot} == 0;
 
-    my $vga = $conf->{vga};
-    if (!$vga) {
-       if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || $conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) {
-           $vga = 'std';
-       } else {
-           $vga = 'cirrus';
-       }
-    }
-
     push @$cmd, '-vga', $vga if $vga; # for kvm 77 and later
 
     # time drift fix
@@ -2367,7 +2399,7 @@ sub config_to_command {
            }
        }
 
-       if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' || 
+       if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' ||
            $ost eq 'wvista') {
            push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
            push @$cmd, '-no-hpet';
@@ -2406,7 +2438,7 @@ sub config_to_command {
 
     push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
 
-    $cpu .= ",".join(',', @$cpuFlags) if scalar(@$cpuFlags);
+    $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
 
     push @$cmd, '-cpu', $cpu;
 
@@ -2429,16 +2461,14 @@ sub config_to_command {
        push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
     }
 
-    if ($vga eq 'qxl') {
+    my $spice_port;
+    if (vga_conf_has_spice($vga)) {
        my $pciaddr = print_pci_addr("spice", $bridges);
 
-       # todo: enable tls
-       #my $x509 = "x509-key-file=/etc/pve/local/pve-ssl.key";
-       #$x509 .= ",x509-cert-file=/etc/pve/local/pve-ssl.pem";
-       #$x509 .= ",x509-cacert-file=/etc/pve/pve-root-ca.pem";
+       $spice_port = PVE::Tools::next_unused_port(61000, 61099);
+
+       push @$cmd, '-spice', "tls-port=${spice_port},addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on";
 
-       my $socket = spice_socket($vmid);
-       push @$cmd, '-spice', "unix=$socket";
        push @$cmd, '-device', "virtio-serial,id=spice$pciaddr";
        push @$cmd, '-chardev', "spicevmc,id=vdagent,name=vdagent";
        push @$cmd, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
@@ -2552,14 +2582,14 @@ sub config_to_command {
     }
 
     push @$cmd, @$devices;
-    push @$cmd, '-rtc', join(',', @$rtcFlags) 
+    push @$cmd, '-rtc', join(',', @$rtcFlags)
        if scalar(@$rtcFlags);
-    push @$cmd, '-machine', join(',', @$machineFlags) 
+    push @$cmd, '-machine', join(',', @$machineFlags)
        if scalar(@$machineFlags);
     push @$cmd, '-global', join(',', @$globalFlags)
        if scalar(@$globalFlags);
 
-    return wantarray ? ($cmd, $vollist) : $cmd;
+    return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
 }
 
 sub vnc_socket {
@@ -2567,9 +2597,12 @@ sub vnc_socket {
     return "${var_run_tmpdir}/$vmid.vnc";
 }
 
-sub spice_socket {
+sub spice_port {
     my ($vmid) = @_;
-    return "${var_run_tmpdir}/$vmid.spice"; 
+
+    my $res = vm_mon_cmd($vmid, 'query-spice');
+
+    return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
 }
 
 sub qmp_socket {
@@ -3001,7 +3034,7 @@ sub qga_unfreezefs {
 }
 
 sub vm_start {
-    my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine) = @_;
+    my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine, $spice_ticket) = @_;
 
     lock_config($vmid, sub {
        my $conf = load_config($vmid, $migratedfrom);
@@ -3017,7 +3050,7 @@ sub vm_start {
        # set environment variable useful inside network script
        $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
 
-       my ($cmd, $vollist) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
+       my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
        my $migrate_port = 0;
 
@@ -3059,20 +3092,29 @@ sub vm_start {
            warn $@ if $@;
        }
 
-       if($migratedfrom) {
+       if ($migratedfrom) {
            my $capabilities = {};
            $capabilities->{capability} =  "xbzrle";
            $capabilities->{state} = JSON::true;
            eval { vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
-       }
-       else{
+           warn $@ if $@;
+           
+           if ($spice_port) {
+               print "spice listens on port $spice_port\n";
+               if ($spice_ticket) {
+                   PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "set_password", protocol => 'spice', password => $spice_ticket);
+                   PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "expire_password", protocol => 'spice', time => "+30");
+               }
+           }
+
+       } else {
 
            if (!$statefile && (!defined($conf->{balloon}) || $conf->{balloon})) {
-               vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024) 
+               vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
                    if $conf->{balloon};
-               vm_mon_cmd_nocheck($vmid, 'qom-set', 
-                           path => "machine/peripheral/balloon0", 
-                           property => "guest-stats-polling-interval", 
+               vm_mon_cmd_nocheck($vmid, 'qom-set',
+                           path => "machine/peripheral/balloon0",
+                           property => "guest-stats-polling-interval",
                            value => 2);
            }
        }
@@ -3103,7 +3145,7 @@ sub vm_qmp_command {
        $timeout = $cmd->{arguments}->{timeout};
        delete $cmd->{arguments}->{timeout};
     }
+
     eval {
        die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
        my $sname = qmp_socket($vmid);
@@ -3697,7 +3739,7 @@ sub get_used_paths {
 
 sub update_disksize {
     my ($vmid, $conf, $volid_hash) = @_;
+
     my $changes;
 
     my $used = {};
@@ -3707,7 +3749,7 @@ sub update_disksize {
     # to the same path).
 
     my $usedpath = {};
-    
+
     # update size info
     foreach my $opt (keys %$conf) {
        if (valid_drivename($opt)) {
@@ -3716,7 +3758,7 @@ sub update_disksize {
            next if !$volid;
 
            $used->{$volid} = 1;
-           if ($volid_hash->{$volid} && 
+           if ($volid_hash->{$volid} &&
                (my $path = $volid_hash->{$volid}->{path})) {
                $usedpath->{$path} = 1;
            }
@@ -3738,7 +3780,7 @@ sub update_disksize {
        next if $opt !~ m/^unused\d+$/;
        my $volid = $conf->{$opt};
        my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
-       if ($used->{$volid} || ($path && $usedpath->{$path})) { 
+       if ($used->{$volid} || ($path && $usedpath->{$path})) {
            $changes = 1;
            delete $conf->{$opt};
        }
@@ -3769,7 +3811,7 @@ sub rescan {
        my ($vmid) = @_;
 
        my $conf = load_config($vmid);
-           
+
        check_lock($conf);
 
        my $vm_volids = {};
@@ -3796,7 +3838,7 @@ sub rescan {
                &$updatefn($vmid);
            } else {
                lock_config($vmid, $updatefn, $vmid);
-           }    
+           }
        }
     }
 }
@@ -3818,7 +3860,7 @@ sub restore_vma_archive {
        } else {
            die "unknown compression method '$comp'\n";
        }
-       
+
     }
 
     my $tmpdir = "/var/tmp/vzdumptmp$$";
@@ -3877,7 +3919,7 @@ sub restore_vma_archive {
                $devinfo->{$devname}->{format} = $format;
                $devinfo->{$devname}->{storeid} = $storeid;
 
-               # check permission on storage 
+               # check permission on storage
                my $pool = $opts->{pool}; # todo: do we need that?
                if ($user ne 'root@pam') {
                    $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
@@ -3888,14 +3930,14 @@ sub restore_vma_archive {
        }
 
        foreach my $devname (keys %$devinfo) {
-           die "found no device mapping information for device '$devname'\n" 
-               if !$devinfo->{$devname}->{virtdev};        
+           die "found no device mapping information for device '$devname'\n"
+               if !$devinfo->{$devname}->{virtdev};
        }
 
        my $cfg = cfs_read_file('storage.cfg');
 
        # create empty/temp config
-       if ($oldconf) { 
+       if ($oldconf) {
            PVE::Tools::file_set_contents($conffile, "memory: 128\n");
            foreach_drive($oldconf, sub {
                my ($ds, $drive) = @_;
@@ -3936,7 +3978,7 @@ sub restore_vma_archive {
 
            my $write_zeros = 1;
            # fixme: what other storages types initialize volumes with zero?
-           if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || 
+           if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' ||
                $scfg->{type} eq 'sheepdog' || $scfg->{type} eq 'rbd') {
                $write_zeros = 0;
            }
@@ -3954,7 +3996,7 @@ sub restore_vma_archive {
 
        my $cookie = { netcount => 0 };
        while (defined(my $line = <$fh>)) {
-           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});  
+           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
        }
 
        $fh->close();
@@ -3987,7 +4029,7 @@ sub restore_vma_archive {
                close($fifofh);
            }
        };
+
        print "restore vma archive: $cmd\n";
        run_command($cmd, input => $input, outfunc => $parser, afterfork => $openfifo);
     };
@@ -4111,7 +4153,7 @@ sub restore_tar_archive {
 
        my $cookie = { netcount => 0 };
        while (defined (my $line = <$srcfd>)) {
-           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});  
+           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
        }
 
        $srcfd->close();
@@ -4159,7 +4201,7 @@ my $snapshot_copy_config = sub {
        next if $k eq 'digest';
        next if $k eq 'description';
        next if $k =~ m/^unused\d+$/;
-               
+
        $dest->{$k} = $source->{$k};
     }
 };
@@ -4198,7 +4240,7 @@ sub foreach_writable_storage {
        my $volid = $drive->{file};
 
        my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
-       $sidhash->{$sid} = $sid if $sid;        
+       $sidhash->{$sid} = $sid if $sid;
     }
 
     foreach my $sid (sort keys %$sidhash) {
@@ -4208,7 +4250,7 @@ sub foreach_writable_storage {
 
 my $alloc_vmstate_volid = sub {
     my ($storecfg, $vmid, $conf, $snapname) = @_;
-    
+
     # Note: we try to be smart when selecting a $target storage
 
     my $target;
@@ -4256,15 +4298,15 @@ my $snapshot_prepare = sub {
 
        my $conf = load_config($vmid);
 
-       die "you can't take a snapshot if it's a template\n" 
+       die "you can't take a snapshot if it's a template\n"
            if is_template($conf);
 
        check_lock($conf);
 
        $conf->{lock} = 'snapshot';
 
-       die "snapshot name '$snapname' already used\n" 
-           if defined($conf->{snapshots}->{$snapname}); 
+       die "snapshot name '$snapname' already used\n"
+           if defined($conf->{snapshots}->{$snapname});
 
        my $storecfg = PVE::Storage::config();
        die "snapshot feature is not available" if !has_feature('snapshot', $conf, $storecfg);
@@ -4300,16 +4342,16 @@ my $snapshot_commit = sub {
 
        my $conf = load_config($vmid);
 
-       die "missing snapshot lock\n" 
-           if !($conf->{lock} && $conf->{lock} eq 'snapshot'); 
+       die "missing snapshot lock\n"
+           if !($conf->{lock} && $conf->{lock} eq 'snapshot');
 
        my $snap = $conf->{snapshots}->{$snapname};
 
-       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+       die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+       die "wrong snapshot state\n"
+           if !($snap->{snapstate} && $snap->{snapstate} eq "prepare");
 
-       die "wrong snapshot state\n" 
-           if !($snap->{snapstate} && $snap->{snapstate} eq "prepare"); 
-       
        delete $snap->{snapstate};
        delete $conf->{lock};
 
@@ -4331,7 +4373,7 @@ sub snapshot_rollback {
     my $prepare = 1;
 
     my $storecfg = PVE::Storage::config();
+
     my $updatefn = sub {
 
        my $conf = load_config($vmid);
@@ -4340,9 +4382,9 @@ sub snapshot_rollback {
 
        $snap = $conf->{snapshots}->{$snapname};
 
-       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+       die "snapshot '$snapname' does not exist\n" if !defined($snap);
 
-       die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n" 
+       die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
            if $snap->{snapstate};
 
        if ($prepare) {
@@ -4372,7 +4414,7 @@ sub snapshot_rollback {
            # Note: old code did not store 'machine', so we try to be smart
            # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4).
            $forcemachine = $conf->{machine} || 'pc-i440fx-1.4';
-           # we remove the 'machine' configuration if not explicitly specified 
+           # we remove the 'machine' configuration if not explicitly specified
            # in the original config.
            delete $conf->{machine} if $snap->{vmstate} && !$has_machine_config;
        }
@@ -4386,7 +4428,7 @@ sub snapshot_rollback {
     };
 
     lock_config($vmid, $updatefn);
-    
+
     foreach_drive($snap, sub {
        my ($ds, $drive) = @_;
 
@@ -4438,7 +4480,7 @@ sub snapshot_create {
 
        if ($running) {
            if ($snap->{vmstate}) {
-               my $path = PVE::Storage::path($storecfg, $snap->{vmstate});     
+               my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
                vm_mon_cmd($vmid, "savevm-start", statefile => $path);
                &$savevm_wait($vmid);
            } else {
@@ -4447,7 +4489,7 @@ sub snapshot_create {
        };
 
        qga_freezefs($vmid) if $running && $freezefs;
+
        foreach_drive($snap, sub {
            my ($ds, $drive) = @_;
 
@@ -4498,7 +4540,7 @@ sub snapshot_delete {
            }
        }
     };
+
     my $updatefn =  sub {
        my ($remove_drive) = @_;
 
@@ -4506,13 +4548,13 @@ sub snapshot_delete {
 
        if (!$drivehash) {
            check_lock($conf);
-           die "you can't delete a snapshot if vm is a template\n" 
+           die "you can't delete a snapshot if vm is a template\n"
                if is_template($conf);
        }
 
        $snap = $conf->{snapshots}->{$snapname};
 
-       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+       die "snapshot '$snapname' does not exist\n" if !defined($snap);
 
        # remove parent refs
        &$unlink_parent($conf, $snap->{parent});
@@ -4677,7 +4719,7 @@ sub qemu_img_format {
        return $1;
     } elsif ($scfg->{type} eq 'iscsi') {
        return "host_device";
-    } else { 
+    } else {
        return "raw";
     }
 }
@@ -4703,12 +4745,12 @@ sub qemu_drive_mirror {
        my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
 
        if ($format) {
-           #fixme : sometime drive-mirror timeout, but works fine after. 
+           #fixme : sometime drive-mirror timeout, but works fine after.
            # (I have see the problem with big volume > 200GB), so we need to eval
-           eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing", 
+           eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
                              sync => "full", target => $dst_path, format => $format); };
        } else {
-           eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing", 
+           eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
                              sync => "full", target => $dst_path); };
        }
 
@@ -4744,9 +4786,9 @@ sub qemu_drive_mirror {
                $old_len = $stat->{offset};
                sleep 1;
            }
-       
+
            if ($vmiddst == $vmid) {
-               # switch the disk if source and destination are on the same guest 
+               # switch the disk if source and destination are on the same guest
                vm_mon_cmd($vmid, "block-job-complete", device => "drive-$drive");
            }
        };
@@ -4763,7 +4805,7 @@ sub qemu_drive_mirror {
 }
 
 sub clone_disk {
-    my ($storecfg, $vmid, $running, $drivename, $drive, $snapname, 
+    my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
        $newvmid, $storage, $format, $full, $newvollist) = @_;
 
     my $newvolid;
@@ -4795,7 +4837,7 @@ sub clone_disk {
            qemu_img_convert($drive->{file}, $newvolid, $size, $snapname);
        } else {
            qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid);
-       }       
+       }
     }
 
     my ($size) = PVE::Storage::volume_size_info($storecfg, $newvolid, 3);
@@ -4813,7 +4855,7 @@ sub get_current_qemu_machine {
     my ($vmid) = @_;
 
     my $cmd = { execute => 'query-machines', arguments => {} };
-    my $res = PVE::QemuServer::vm_qmp_command($vmid, $cmd); 
+    my $res = PVE::QemuServer::vm_qmp_command($vmid, $cmd);
 
     my ($current, $default);
     foreach my $e (@$res) {
@@ -4825,4 +4867,21 @@ sub get_current_qemu_machine {
     return $current || $default || 'pc';
 }
 
+sub read_x509_subject_spice {
+    my ($filename) = @_;
+
+    # read x509 subject
+    my $bio = Net::SSLeay::BIO_new_file($filename, 'r');
+    my $x509 = Net::SSLeay::PEM_read_bio_X509($bio);
+    Net::SSLeay::BIO_free($bio);
+    my $nameobj = Net::SSLeay::X509_get_subject_name($x509);
+    my $subject = Net::SSLeay::X509_NAME_oneline($nameobj);
+    Net::SSLeay::X509_free($x509);
+  
+    # remote-viewer wants comma as seperator (not '/')
+    $subject =~ s!^/!!;
+    $subject =~ s!/(\w+=)!,$1!g;
+
+    return $subject;
+}
 1;