Allow xm to spawn vnc viewer

The new merged qemu no longer has the ability to spawn a vnc viewer
process in the bowels of the xend/qemu stack.  In this patch we
provide support for this use case in a different manner - one more
akin to the mechanism used for `xm console' and `xm create -c'.

We introduce new xm options:
   xm create --vncviewer [--vncviewer-autopass]
   xm vncviewer [--vncviewer-autopass]

These spawn a VNC viewer, obtaining the relevant information
(including the port number and if you tell it your viewer supports it
the password to use) directly from xenstore.

Like xm console it waits in the foreground for the vnc port to become
available; the timeout case isn't handled as well as it might be - it
just causes the whole program (xm) to die with `Alarm clock' but this
is difficult to deal with given the current structure of the xs Python
lowlevel interface, which doesn't provide a timeout on the call to
wait for a xenstore watch.

Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
This commit is contained in:
Keir Fraser committed 2008-07-30 16:22:45 +01:00
1 parent fb044093f6
commit cd1f9abc4a
4 files changed
+183 -50

No files matched your search

+44
View File
@@ -1,6 +1,50 @@
import traceback
import sys
import os
def exception_string(e):
(ty,v,tb) = sys.exc_info()
return traceback.format_exception_only(ty,v)
def daemonize(prog, args, stdin_tmpfile=None):
"""Runs a program as a daemon with the list of arguments. Returns the PID
of the daemonized program, or returns 0 on error.
"""
r, w = os.pipe()
pid = os.fork()
if pid == 0:
os.close(r)
w = os.fdopen(w, 'w')
os.setsid()
try:
pid2 = os.fork()
except:
pid2 = None
if pid2 == 0:
os.chdir("/")
null_fd = os.open("/dev/null", os.O_RDWR)
if stdin_tmpfile is not None:
os.dup2(stdin_tmpfile.fileno(), 0)
else:
os.dup2(null_fd, 0)
os.dup2(null_fd, 1)
os.dup2(null_fd, 2)
for fd in range(3, 256):
try:
os.close(fd)
except:
pass
os.execvp(prog, args)
os._exit(1)
else:
w.write(str(pid2 or 0))
w.close()
os._exit(0)
os.close(w)
r = os.fdopen(r)
daemon_pid = int(r.read())
r.close()
os.waitpid(pid, 0)
return daemon_pid
+63 -2
View File
@@ -15,10 +15,71 @@
# Copyright (C) 2005 XenSource Ltd
#============================================================================
import xen.util.auxbin
import xen.lowlevel.xs
import os
import sys
import signal
from xen.util import utils
XENCONSOLE = "xenconsole"
import xen.util.auxbin
def execConsole(domid):
xen.util.auxbin.execute(XENCONSOLE, [str(domid)])
class OurXenstoreConnection:
def __init__(self):
self.handle = xen.lowlevel.xs.xs()
def read_eventually(self, path):
watch = None
trans = None
try:
signal.alarm(10)
watch = self.handle.watch(path, None)
while True:
result = self.handle.read('0', path)
if result is not None:
return result
self.handle.read_watch()
self.handle.unwatch(path, watch)
signal.alarm(0)
except:
signal.alarm(0)
if watch is not None: self.handle.unwatch(path, watch)
raise
def read_maybe(self, path):
return self.handle.read('0', path)
def runVncViewer(domid, do_autopass, do_daemonize=False):
xs = OurXenstoreConnection()
d = '/local/domain/%d/' % domid
vnc_port = xs.read_eventually(d + 'console/vnc-port')
vfb_backend = xs.read_maybe(d + 'device/vfb/0/backend')
vnc_listen = None
vnc_password = None
vnc_password_tmpfile = None
cmdl = ['vncviewer']
if vfb_backend is not None:
vnc_listen = xs.read_maybe(vfb_backend + '/vnclisten')
if do_autopass:
vnc_password = xs.read_maybe(vfb_backend + '/vncpasswd')
if vnc_password is not None:
cmdl.append('-autopass')
vnc_password_tmpfile = os.tmpfile()
print >>vnc_password_tmpfile, vnc_password
vnc_password_tmpfile.seek(0)
vnc_password_tmpfile.flush()
if vnc_listen is None:
vnc_listen = 'localhost'
cmdl.append('%s:%d' % (vnc_listen, int(vnc_port) - 5900))
if do_daemonize:
pid = utils.daemonize('vncviewer', cmdl, vnc_password_tmpfile)
if pid == 0:
puts >>sys.stderr, 'failed to invoke vncviewer'
os._exit(-1)
else:
print 'invoking ', ' '.join(cmdl)
if vnc_password_tmpfile is not None:
os.dup2(vnc_password_tmpfile.fileno(), 0)
os.execvp('vncviewer', cmdl)
+18 -47
View File
@@ -36,10 +36,12 @@ from xen.util import blkif
from xen.util import vscsi_util
import xen.util.xsm.xsm as security
from xen.xm.main import serverType, SERVER_XEN_API, get_single_vm
from xen.util import utils
from xen.xm.opts import *
from main import server
from main import domain_name_to_domid
import console
@@ -118,6 +120,14 @@ gopts.opt('console_autoconnect', short='c',
fn=set_true, default=0,
use="Connect to the console after the domain is created.")
gopts.opt('vncviewer',
fn=set_true, default=0,
use="Connect to the VNC display after the domain is created.")
gopts.opt('vncviewer-autopass',
fn=set_true, default=0,
use="Pass VNC password to viewer via stdin and -autopass.")
gopts.var('vncpasswd', val='NAME',
fn=set_value, default=None,
use="Password for VNC console on HVM domain.")
@@ -128,7 +138,7 @@ gopts.var('vncviewer', val='no|yes',
"The address of the vncviewer is passed to the domain on the "
"kernel command line using 'VNC_SERVER=<host>:<port>'. The port "
"used by vnc is 5500 + DISPLAY. A display value with a free port "
"is chosen if possible.\nOnly valid when vnc=1.")
"is chosen if possible.\nOnly valid when vnc=1.\nDEPRECATED")
gopts.var('vncconsole', val='no|yes',
fn=set_bool, default=None,
@@ -1108,44 +1118,6 @@ def choose_vnc_display():
return None
vncpid = None
def daemonize(prog, args):
"""Runs a program as a daemon with the list of arguments. Returns the PID
of the daemonized program, or returns 0 on error.
"""
r, w = os.pipe()
pid = os.fork()
if pid == 0:
os.close(r)
w = os.fdopen(w, 'w')
os.setsid()
try:
pid2 = os.fork()
except:
pid2 = None
if pid2 == 0:
os.chdir("/")
for fd in range(0, 256):
try:
os.close(fd)
except:
pass
os.open("/dev/null", os.O_RDWR)
os.dup2(0, 1)
os.dup2(0, 2)
os.execvp(prog, args)
os._exit(1)
else:
w.write(str(pid2 or 0))
w.close()
os._exit(0)
os.close(w)
r = os.fdopen(r)
daemon_pid = int(r.read())
r.close()
os.waitpid(pid, 0)
return daemon_pid
def spawn_vnc(display):
"""Spawns a vncviewer that listens on the specified display. On success,
returns the port that the vncviewer is listening on and sets the global
@@ -1154,7 +1126,7 @@ def spawn_vnc(display):
vncargs = (["vncviewer", "-log", "*:stdout:0",
"-listen", "%d" % (VNC_BASE_PORT + display) ])
global vncpid
vncpid = daemonize("vncviewer", vncargs)
vncpid = utils.daemonize("vncviewer", vncargs)
if vncpid == 0:
return 0
@@ -1362,6 +1334,11 @@ def main(argv):
elif not opts.is_xml:
dom = make_domain(opts, config)
if opts.vals.vncviewer:
domid = domain_name_to_domid(sxp.child_value(config, 'name', -1))
vncviewer_autopass = getattr(opts.vals,'vncviewer-autopass', False)
console.runVncViewer(domid, vncviewer_autopass, True)
def do_console(domain_name):
cpid = os.fork()
if cpid != 0:
@@ -1373,13 +1350,7 @@ def do_console(domain_name):
if os.WEXITSTATUS(rv) != 0:
sys.exit(os.WEXITSTATUS(rv))
try:
# Acquire the console of the created dom
if serverType == SERVER_XEN_API:
domid = server.xenapi.VM.get_domid(
get_single_vm(domain_name))
else:
dom = server.xend.domain(domain_name)
domid = int(sxp.child_value(dom, 'domid', '-1'))
domid = domain_name_to_domid(domain_name)
console.execConsole(domid)
except:
pass
+58 -1
View File
@@ -64,6 +64,9 @@ import inspect
from xen.xend import XendOptions
xoptions = XendOptions.instance()
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
# getopt.gnu_getopt is better, but only exists in Python 2.3+. Use
# getopt.getopt if gnu_getopt is not available. This will mean that options
# may only be specified before positional arguments.
@@ -97,6 +100,8 @@ SUBCOMMAND_HELP = {
'console' : ('[-q|--quiet] <Domain>',
'Attach to <Domain>\'s console.'),
'vncviewer' : ('[--[vncviewer-]autopass] <Domain>',
'Attach to <Domain>\'s VNC server.'),
'create' : ('<ConfigFile> [options] [vars]',
'Create a domain based on <ConfigFile>.'),
'destroy' : ('<Domain>',
@@ -243,6 +248,10 @@ SUBCOMMAND_OPTIONS = {
'console': (
('-q', '--quiet', 'Do not print an error message if the domain does not exist'),
),
'vncviewer': (
('', '--autopass', 'Pass VNC password to viewer via stdin and -autopass'),
('', '--vncviewer-autopass', '(consistency alias for --autopass)'),
),
'dmesg': (
('-c', '--clear', 'Clear dmesg buffer as well as printing it'),
),
@@ -260,6 +269,8 @@ SUBCOMMAND_OPTIONS = {
'start': (
('-p', '--paused', 'Do not unpause domain after starting it'),
('-c', '--console_autoconnect', 'Connect to the console after the domain is created'),
('', '--vncviewer', 'Connect to display via VNC after the domain is created'),
('', '--vncviewer-autopass', 'Pass VNC password to viewer via stdin and -autopass'),
),
'resume': (
('-p', '--paused', 'Do not unpause domain after resuming it'),
@@ -277,6 +288,7 @@ SUBCOMMAND_OPTIONS = {
common_commands = [
"console",
"vncviewer",
"create",
"new",
"delete",
@@ -304,6 +316,7 @@ common_commands = [
domain_commands = [
"console",
"vncviewer",
"create",
"new",
"delete",
@@ -1185,14 +1198,20 @@ def xm_start(args):
paused = False
console_autoconnect = False
vncviewer = False
vncviewer_autopass = False
try:
(options, params) = getopt.gnu_getopt(args, 'cp', ['console_autoconnect','paused'])
(options, params) = getopt.gnu_getopt(args, 'cp', ['console_autoconnect','paused','vncviewer','vncviewer-autopass'])
for (k, v) in options:
if k in ('-p', '--paused'):
paused = True
if k in ('-c', '--console_autoconnect'):
console_autoconnect = True
if k in ('--vncviewer'):
vncviewer = True
if k in ('--vncviewer-autopass'):
vncviewer_autopass = True
if len(params) != 1:
raise OptionError("Expects 1 argument")
@@ -1205,6 +1224,9 @@ def xm_start(args):
if console_autoconnect:
start_do_console(dom)
if console_autoconnect:
console.runVncViewer(domid, vncviewer_autopass, True)
try:
if serverType == SERVER_XEN_API:
server.xenapi.VM.start(get_single_vm(dom), paused)
@@ -1783,6 +1805,40 @@ def xm_console(args):
console.execConsole(domid)
def domain_name_to_domid(domain_name):
if serverType == SERVER_XEN_API:
domid = server.xenapi.VM.get_domid(
get_single_vm(domain_name))
else:
dom = server.xend.domain(domain_name)
domid = int(sxp.child_value(dom, 'domid', '-1'))
return domid
def xm_vncviewer(args):
autopass = False;
try:
(options, params) = getopt.gnu_getopt(args, '', ['autopass','vncviewer-autopass'])
except getopt.GetoptError, opterr:
err(opterr)
usage('vncviewer')
for (k, v) in options:
if k in ['--autopass','--vncviewer-autopass']:
autopass = True
else:
assert False
if len(params) != 1:
err('No domain given (or several parameters specified)')
usage('vncviewer')
dom = params[0]
domid = domain_name_to_domid(dom)
console.runVncViewer(domid, autopass)
def xm_uptime(args):
short_mode = 0
@@ -2617,6 +2673,7 @@ commands = {
"event-monitor": xm_event_monitor,
# console commands
"console": xm_console,
"vncviewer": xm_vncviewer,
# xenstat commands
"top": xm_top,
# domain commands
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.