Commit 9c1493d4 authored by Dietmar Maurer's avatar Dietmar Maurer

add spiceproxy server

parent c1189c52
......@@ -11,6 +11,7 @@ use File::stat qw();
use Digest::MD5;
# use AnyEvent::Strict; # only use this for debugging
use AnyEvent::Util qw(guard fh_nonblocking WSAEWOULDBLOCK WSAEINPROGRESS);
use AnyEvent::Socket;
use AnyEvent::Handle;
use AnyEvent::TLS;
use AnyEvent::IO;
......@@ -215,7 +216,7 @@ sub response {
#print "SEND(without content) $res\n" if $self->{debug};
$res .= "\015\012";
$res .= $content;
$res .= $content if $content;
$self->log_request($reqstate, $reqstate->{request});
......@@ -475,6 +476,94 @@ sub handle_api2_request {
}
}
sub handle_spice_proxy_request {
my ($self, $reqstate, $vmid, $node) = @_;
eval {
my $remip;
if ($node ne 'localhost' && $node ne PVE::INotify::nodename()) {
$remip = PVE::Cluster::remote_node_ip($node);
die "unable to get remote IP address for none '$node'\n";
}
if ($remip) {
die "not implemented";
return;
}
$reqstate->{hdl}->timeout(0);
# local node
my $socket = PVE::QemuServer::spice_socket($vmid);
print "$$: CONNECT $vmid, $node, $socket\n" if $self->{debug};
# fixme: this needs root privs
tcp_connect "unix/", $socket, sub {
my ($fh) = @_
or die "connect to '$socket' failed: $!";
print "$$: CONNECTed to $socket\n" if $self->{debug};
$reqstate->{proxyhdl} = AnyEvent::Handle->new(
fh => $fh,
rbuf_max => 64*1024,
wbuf_max => 64*10*1024,
timeout => 0,
#linger => 0, # avoid problems with ssh - really needed ?
on_eof => sub {
my ($hdl) = @_;
eval {
$self->log_aborted_request($reqstate);
$self->client_do_disconnect($reqstate);
};
if (my $err = $@) { syslog('err', $err); }
},
on_error => sub {
my ($hdl, $fatal, $message) = @_;
eval {
$self->log_aborted_request($reqstate, $message);
$self->client_do_disconnect($reqstate);
};
if (my $err = $@) { syslog('err', "$err"); }
},
on_read => sub {
my ($hdl) = @_;
my $len = length($hdl->{rbuf});
my $data = substr($hdl->{rbuf}, 0, $len, '');
#print "READ1 $len\n";
$reqstate->{hdl}->push_write($data) if $reqstate->{hdl};
});
$reqstate->{hdl}->on_read(sub {
my ($hdl) = @_;
my $len = length($hdl->{rbuf});
my $data = substr($hdl->{rbuf}, 0, $len, '');
#print "READ0 $len\n";
$reqstate->{proxyhdl}->push_write($data) if $reqstate->{proxyhdl};
});
$reqstate->{hdl}->wbuf_max(64*10*1024);
# fixme: use stop_read/start_read if write buffer grows to much
my $proto = $reqstate->{proto} ? $reqstate->{proto}->{str} : 'HTTP/1.0';
my $res = "$proto 200 OK\015\012"; # hope this is the right answer?
$reqstate->{hdl}->push_write($res);
};
};
if (my $err = $@) {
$self->log_aborted_request($reqstate, $err);
$self->client_do_disconnect($reqstate);
}
}
sub handle_request {
my ($self, $reqstate, $auth) = @_;
......@@ -753,7 +842,16 @@ sub unshift_read_header {
# header processing complete - authenticate now
my $auth = {};
if ($path =~ m!$baseuri!) {
if ($self->{spiceproxy}) {
my $connect_str = $r->header('Host');
my ($vmid, $node) = PVE::AccessControl::verify_spice_connect_url($connect_str);
if (!($vmid && $node)) {
$self->error($reqstate, HTTP_UNAUTHORIZED, "invalid ticket");
return;
}
$self->handle_spice_proxy_request($reqstate, $vmid, $node);
return;
} elsif ($path =~ m!$baseuri!) {
my $token = $r->header('CSRFPreventionToken');
my $cookie = $r->header('Cookie');
my $ticket = PVE::REST::extract_auth_cookie($cookie);
......@@ -862,7 +960,7 @@ sub push_request_header {
my ($hdl, $line) = @_;
eval {
# print "got request header: $line\n" if $self->{debug};
#print "got request header: $line\n" if $self->{debug};
$reqstate->{keep_alive}--;
......@@ -1142,6 +1240,10 @@ sub new {
$self->{tls_ctx} = AnyEvent::TLS->new(%{$self->{ssl}});
}
if ($self->{spiceproxy}) {
$known_methods = { CONNECT => 1 };
}
$self->open_access_log($self->{logfile}) if $self->{logfile};
$self->{max_conn_soft_limit} = $self->{max_conn} > 100 ? $self->{max_conn} - 20 : $self->{max_conn};
......
......@@ -12,6 +12,7 @@ SCRIPTS = \
pvectl \
pvedaemon \
pveproxy \
spiceproxy \
pveversion \
pvesubscription \
pvemailforward.pl \
......@@ -25,6 +26,7 @@ MANS = \
pvestatd.1 \
pvedaemon.1 \
pveproxy.1 \
spiceproxy.1 \
pveversion.1 \
pvesubscription.1 \
pveupgrade.1 \
......
......@@ -6,6 +6,7 @@ SCRIPTS = \
pve-manager \
pvedaemon \
pveproxy \
spiceproxy \
pvebanner \
pvestatd \
pvenetcommit
......
#!/bin/sh
### BEGIN INIT INFO
# Provides: spiceproxy
# Required-Start: $remote_fs $network $syslog pveproxy
# Required-Stop: $remote_fs $network $syslog pveproxy
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: PVE SPICE Proxy Server
### END INIT INFO
. /lib/lsb/init-functions
PATH=/sbin:/bin:/usr/bin:/usr/sbin
DAEMON=/usr/bin/spiceproxy
NAME=spiceproxy
DESC="PVE SPICE Proxy Server"
RUNDIR=/var/run/pveproxy
PIDFILE=${RUNDIR}/spiceproxy.pid
test -f $DAEMON || exit 0
# avoid warnings about uninstalled locales when pveproxy executes commands
export LC_ALL="C"
mkdir -p ${RUNDIR} || true
chmod 0700 ${RUNDIR} || true
chown www-data:www-data ${RUNDIR} || true
DAEMON_OPTS=""
# Include defaults if available
if [ -f /etc/default/$NAME ] ; then
ALLOW_FROM=""
DENY_FROM=""
POLICY=""
. /etc/default/$NAME
if [ -n "$ALLOW_FROM" ] ; then
DAEMON_OPTS="${DAEMON_OPTS} --allow-from ${ALLOW_FROM}"
fi
if [ -n "$DENY_FROM" ] ; then
DAEMON_OPTS="${DAEMON_OPTS} --deny-from ${DENY_FROM}"
fi
if [ -n "$POLICY" ] ; then
DAEMON_OPTS="${DAEMON_OPTS} --policy $POLICY"
fi
fi
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- ${DAEMON_OPTS}
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon --stop --quiet --retry TERM/2/TERM/15/KILL/2 --pidfile $PIDFILE
log_end_msg $?
;;
reload)
log_daemon_msg "Reloading $DESC" "$NAME"
if ( [ -e $PIDFILE ] && kill -0 `cat $PIDFILE`) then
start-stop-daemon --stop --signal HUP --pidfile $PIDFILE
else
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- ${DAEMON_OPTS}
fi
log_end_msg $?
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
start-stop-daemon --stop --quiet --retry TERM/2/TERM/15/KILL/2 --pidfile $PIDFILE
sleep 2
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- ${DAEMON_OPTS}
log_end_msg $?
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}"
exit 1
;;
esac
exit 0
#!/usr/bin/perl -w -T
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
use lib '..';
use strict;
use English;
use Getopt::Long;
use PVE::SafeSyslog;
use PVE::APIDaemon;
my $pidfile = "/var/run/pveproxy/spiceproxy.pid";
my $lockfile = "/var/lock/spiceproxy.lck";
my $opt_debug;
initlog ('spiceproxy');
if (!GetOptions ('debug' => \$opt_debug)) {
die "usage: $0 [--debug]\n";
}
$SIG{'__WARN__'} = sub {
my $err = $@;
my $t = $_[0];
chomp $t;
syslog('warning', "WARNING: %s", $t);
$@ = $err;
};
$0 = "spiceproxy";
my $gid = getgrnam('www-data') || die "getgrnam failed - $!\n";
POSIX::setgid($gid) || die "setgid $gid failed - $!\n";
$EGID = "$gid $gid"; # this calls setgroups
my $uid = getpwnam('www-data') || die "getpwnam failed - $!\n";
POSIX::setuid($uid) || die "setuid $uid failed - $!\n";
# just to be sure
die "detected strange uid/gid\n" if !($UID == $uid && $EUID == $uid && $GID eq "$gid $gid" && $EGID eq "$gid $gid");
my $cpid;
my $daemon;
eval {
$daemon = PVE::APIDaemon->new(
port => 3128,
keep_alive => 0,
max_workers => 1, # do we need more?
max_conn => 500,
lockfile => $lockfile,
debug => $opt_debug,
spiceproxy => 1,
logfile => '/var/log/pveproxy/spice.log',
);
};
my $err = $@;
if ($err) {
syslog ('err' , "unable to start server: $err");
print STDERR $err;
exit (-1);
}
if ($opt_debug || !($cpid = fork ())) {
$SIG{PIPE} = 'IGNORE';
$SIG{INT} = 'IGNORE' if !$opt_debug;
$SIG{TERM} = $SIG{QUIT} = sub {
syslog ('info' , "server closing");
$SIG{INT} = 'DEFAULT';
unlink "$pidfile";
exit (0);
};
syslog ('info' , "starting server");
if (!$opt_debug) {
# redirect STDIN/STDOUT/SDTERR to /dev/null
open STDIN, '</dev/null' || die "can't read /dev/null [$!]";
open STDOUT, '>/dev/null' || die "can't write /dev/null [$!]";
open STDERR, '>&STDOUT' || die "can't open STDERR to STDOUT [$!]";
}
POSIX::setsid();
eval {
$daemon->start_server();
};
my $err = $@;
if ($err) {
syslog ('err' , "unexpected server error: $err");
print STDERR $err if $opt_debug;
exit (-1);
}
} else {
open (PIDFILE, ">$pidfile") ||
die "cant write '$pidfile' - $! :ERROR";
print PIDFILE "$cpid\n";
close (PIDFILE) ||
die "cant write '$pidfile' - $! :ERROR";
}
exit (0);
__END__
=head1 NAME
spiceproxy - SPICE proxy server for Proxmox VE
=head1 SYNOPSIS
spiceproxy [--debug]
=head1 DESCRIPTION
SPICE proxy server for Proxmox VE. Listens on port 3128.
=head1 COPYRIGHT AND DISCLAIMER
Copyright (C) 2007-2013 Proxmox Server Solutions GmbH
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with this program. If not, see
<http://www.gnu.org/licenses/>.
/etc/init.d/pve-manager
/etc/init.d/pvedaemon
/etc/init.d/pveproxy
/etc/init.d/spiceproxy
/etc/init.d/pvebanner
/etc/init.d/pvenetcommit
/etc/init.d/pvestatd
......
......@@ -28,6 +28,7 @@ case "$1" in
test -e /proxmox_install_mode || invoke-rc.d pvedaemon restart
test -e /proxmox_install_mode || invoke-rc.d pvestatd restart
test -e /proxmox_install_mode || invoke-rc.d pveproxy restart
test -e /proxmox_install_mode || invoke-rc.d spiceproxy restart
exit 0;;
......@@ -60,6 +61,7 @@ case "$1" in
update-rc.d pvedaemon defaults 21 79 >/dev/null
update-rc.d pveproxy defaults 21 79 >/dev/null
update-rc.d spiceproxy defaults 21 79 >/dev/null
update-rc.d pvestatd defaults 21 79 >/dev/null
update-rc.d pvebanner start 99 2 3 4 5 . >/dev/null
update-rc.d pvenetcommit start 15 S . >/dev/null
......@@ -67,6 +69,7 @@ case "$1" in
test -e /proxmox_install_mode || invoke-rc.d pvedaemon restart
test -e /proxmox_install_mode || invoke-rc.d pveproxy restart
test -e /proxmox_install_mode || invoke-rc.d spiceproxy restart
test -e /proxmox_install_mode || invoke-rc.d pvestatd restart
# rewrite banner
......
......@@ -6,6 +6,7 @@ set -e
if [ "$1" = purge ]; then
update-rc.d pvedaemon remove >/dev/null 2>&1
update-rc.d pveproxy remove >/dev/null 2>&1
update-rc.d spiceproxy remove >/dev/null 2>&1
update-rc.d pvestatd remove >/dev/null 2>&1
update-rc.d pvebanner remove >/dev/null 2>&1
update-rc.d pvenetcommit remove >/dev/null 2>&1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment