diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 076356c7..53629b35 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -497,6 +497,7 @@ __PACKAGE__->register_method({ { subdir => 'rrddata' }, { subdir => 'monitor' }, { subdir => 'snapshot' }, + { subdir => 'spiceproxy' }, ]; return $res; @@ -1321,6 +1322,118 @@ __PACKAGE__->register_method({ }; }}); +__PACKAGE__->register_method({ + name => 'spiceproxy', + path => '{vmid}/spiceproxy', + method => 'GET', # fixme: should be POST, but howto handle that in the HTML client + protected => 1, + proxyto => 'node', # fixme: use direct connections or ssh tunnel? + permissions => { + check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]], + }, + description => "Returns a SPICE configuration to connect to the VM.", + parameters => { + additionalProperties => 0, + properties => { + node => get_standard_option('pve-node'), + vmid => get_standard_option('pve-vmid'), + }, + }, + returns => { + additionalProperties => 1, + properties => { + type => { type => 'string' }, + password => { type => 'string' }, + host => { type => 'string' }, + port => { type => 'integer' }, + }, + }, + code => sub { + my ($param) = @_; + + my $rpcenv = PVE::RPCEnvironment::get(); + + my $authuser = $rpcenv->get_user(); + + my $vmid = $param->{vmid}; + my $node = $param->{node}; + + my $port = PVE::Tools::next_vnc_port(); + + my $remip; + + # Note: we currectly use "proxyto => 'node'", so this code will never trigger + if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) { + $remip = PVE::Cluster::remote_node_ip($node); + } + + my $authpath = "/vms/$vmid"; + + my $ticket = PVE::AccessControl::assemble_spice_ticket($authuser, $authpath); + + my $timeout = 10; + + # Note: this only works if VM is on local node + PVE::QemuServer::vm_mon_cmd($vmid, "set_password", protocol => 'spice', password => $ticket); + PVE::QemuServer::vm_mon_cmd($vmid, "expire_password", protocol => 'spice', time => "+30"); + + my $remcmd = []; #fixme + + my $realcmd = sub { + my $upid = shift; + + syslog('info', "starting spice proxy $upid\n"); + + my $socket = PVE::QemuServer::spice_socket($vmid); + + my $cmd = ['/usr/bin/socat', '-d', '-d', + "TCP-LISTEN:$port,reuseaddr,fork" ]; + + if ($remip) { + push @$cmd, "EXEC:'ssh root@$remip socat STDIO UNIX-CONNECT:$socket"; + } else { + push @$cmd, "UNIX-CONNECT:$socket"; + } + + my $conn_count = 0; + + my $parser = sub { + my $line = shift; + print "$line\n"; + if ($line =~ /successfully connected from/) { + $conn_count++; + } elsif ($line =~ /exiting with status/) { + $conn_count--; + # Note: counting connections seems unreliable here + die "client exit\n"; # if $conn_count <= 0; + } + }; + + eval { PVE::Tools::run_command($cmd, errfunc => $parser, outfunc => sub{}); }; + if (my $err = $@) { + die $err if $err !~ m/client exit$/; + } + + return; + }; + + my $upid = $rpcenv->fork_worker('spiceproxy', $vmid, $authuser, $realcmd); + + PVE::Tools::wait_for_vnc_port($port); + + # fimxe: ?? + my $host = `hostname -f` || PVE::INotify::nodename(); + chomp $host; + + return { + type => 'spice', + host => $host, + port => $port, + password => $ticket, + upid => $upid, + }; + }}); + __PACKAGE__->register_method({ name => 'vmcmdidx', path => '{vmid}/status', diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index e2b6ec83..31d8103b 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -2424,7 +2424,7 @@ sub config_to_command { my $socket = spice_socket($vmid); - push @$cmd, '-spice', "disable-ticketing,unix=$socket"; + 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";