zone-vnc-console revision 4626
d5b7ba26785d7494166d48876362ba30ff30b98awrowe#!/usr/bin/python2.7
c30ef289fe64ac7fedc44cfcc6b439f0f8458b4cgregames
c30ef289fe64ac7fedc44cfcc6b439f0f8458b4cgregames# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
396aeca634b86a3ab34d5bdb9c32cbce73c72421jerenkrantz#
ab8c0315521735c73ce16c8072f91e17c406ca5bnd# Licensed under the Apache License, Version 2.0 (the "License"); you may
ab8c0315521735c73ce16c8072f91e17c406ca5bnd# not use this file except in compliance with the License. You may obtain
ab8c0315521735c73ce16c8072f91e17c406ca5bnd# a copy of the License at
b9e99e0d3154bbebe3e1b8d11d6c15bde79510a5nd#
b9e99e0d3154bbebe3e1b8d11d6c15bde79510a5nd# http://www.apache.org/licenses/LICENSE-2.0
b9e99e0d3154bbebe3e1b8d11d6c15bde79510a5nd#
b9e99e0d3154bbebe3e1b8d11d6c15bde79510a5nd# Unless required by applicable law or agreed to in writing, software
ea5f8cfbb7ef1d19318f6994c26dd73c38ffd8ddjerenkrantz# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
ea5f8cfbb7ef1d19318f6994c26dd73c38ffd8ddjerenkrantz# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
ea5f8cfbb7ef1d19318f6994c26dd73c38ffd8ddjerenkrantz# License for the specific language governing permissions and limitations
4567cfc6a65328bd3e8dd2b758ca926b389c7058brianp# under the License.
4567cfc6a65328bd3e8dd2b758ca926b389c7058brianp
4567cfc6a65328bd3e8dd2b758ca926b389c7058brianpimport ConfigParser
3068cf5757c8bdbea77e8f6805686aa0b0241a17ndimport contextlib
3068cf5757c8bdbea77e8f6805686aa0b0241a17ndimport errno
3068cf5757c8bdbea77e8f6805686aa0b0241a17ndimport fcntl
3068cf5757c8bdbea77e8f6805686aa0b0241a17ndimport os
396aeca634b86a3ab34d5bdb9c32cbce73c72421jerenkrantzimport pwd
396aeca634b86a3ab34d5bdb9c32cbce73c72421jerenkrantzimport smf_include
396aeca634b86a3ab34d5bdb9c32cbce73c72421jerenkrantzimport socket
4cdc5446050c19b9d519a273a129188586e8d445jerenkrantzimport subprocess
4cdc5446050c19b9d519a273a129188586e8d445jerenkrantzimport sys
4cdc5446050c19b9d519a273a129188586e8d445jerenkrantzimport tempfile
e0b93afc77decfbc0aab461b08ee224a0af89af2rederpjimport time
e0b93afc77decfbc0aab461b08ee224a0af89af2rederpj
e0b93afc77decfbc0aab461b08ee224a0af89af2rederpjfrom oslo_config import cfg
e0b93afc77decfbc0aab461b08ee224a0af89af2rederpj
e0b93afc77decfbc0aab461b08ee224a0af89af2rederpjGTF = "/usr/bin/gtf"
f5610d5460e701dd3f3514395867a6b5241fda81bnicholesSVCADM = "/usr/sbin/svcadm"
f5610d5460e701dd3f3514395867a6b5241fda81bnicholesSVCCFG = "/usr/sbin/svccfg"
f5610d5460e701dd3f3514395867a6b5241fda81bnicholesSVCPROP = "/usr/bin/svcprop"
f5610d5460e701dd3f3514395867a6b5241fda81bnicholesVNCSERVER = "/usr/bin/vncserver"
f5610d5460e701dd3f3514395867a6b5241fda81bnicholesXRANDR = "/usr/bin/xrandr"
f5610d5460e701dd3f3514395867a6b5241fda81bnicholesNOVACFG = "/etc/nova/nova.conf"
f5610d5460e701dd3f3514395867a6b5241fda81bnicholesXSTARTUPHDR = "# WARNING: THIS FILE GENERATED BY SMF.\n" + \
c30ef289fe64ac7fedc44cfcc6b439f0f8458b4cgregames "# DO NOT EDIT THIS FILE. EDITS WILL BE LOST.\n"
c30ef289fe64ac7fedc44cfcc6b439f0f8458b4cgregamesXRESOURCES = "[[ -f ~/.Xresources ]] && /usr/bin/xrdb -merge ~/.Xresources\n"
c30ef289fe64ac7fedc44cfcc6b439f0f8458b4cgregamesXTERM = "/usr/bin/xterm"
c30ef289fe64ac7fedc44cfcc6b439f0f8458b4cgregames# Borderless, Monospsce font, point size 14, white foreground on black
c30ef289fe64ac7fedc44cfcc6b439f0f8458b4cgregames# background are reasonable defaults.
2f408250e9111c4b85b2b4b9b8836e83987efdefstoddardXTERMOPTS = ' -b 0 -fa Monospace -fs 14 -fg white -bg black -title ' + \
2f408250e9111c4b85b2b4b9b8836e83987efdefstoddard '"Zone Console: $ZONENAME"'
2f408250e9111c4b85b2b4b9b8836e83987efdefstoddardXWININFO = "/usr/bin/xwininfo"
2f408250e9111c4b85b2b4b9b8836e83987efdefstoddard
d5b7ba26785d7494166d48876362ba30ff30b98awrowe# Port ranges allocated for VNC and X11 sockets.
47fe07199bddec6124ab7251c6be5c6c9ac00485jerenkrantzVNCPORT_START = 5900
47fe07199bddec6124ab7251c6be5c6c9ac00485jerenkrantzVNCPORT_END = 5999
c1bf42dc465137de1fdb8f3d9d1c3e4d2db5c003brianpX11PORT_START = 6000
5a42079659ea008632642edc7fe18f9517cfea2aminfrin
c1bf42dc465137de1fdb8f3d9d1c3e4d2db5c003brianp# Enclose command in comments to prevent xterm consuming zlogin opts
6646a289c2d4778c8cd43d62b5a1cc966a356f85jerenkrantzZLOGINOPTS = ' -e "/usr/bin/pfexec /usr/sbin/zlogin -C -E $ZONENAME"\n'
6646a289c2d4778c8cd43d62b5a1cc966a356f85jerenkrantzXSTARTUP = XSTARTUPHDR + XRESOURCES + XTERM + XTERMOPTS + ZLOGINOPTS
6646a289c2d4778c8cd43d62b5a1cc966a356f85jerenkrantz
6646a289c2d4778c8cd43d62b5a1cc966a356f85jerenkrantzCONF = cfg.CONF
aec70520ebe1e33e0d5e83c3626649d2a41dbe68wroweCONF.import_opt('vncserver_listen', 'nova.vnc')
aec70520ebe1e33e0d5e83c3626649d2a41dbe68wrowe
aec70520ebe1e33e0d5e83c3626649d2a41dbe68wrowe
ad451e2e428a069086d1c18c9e3372f8846ec617wrowedef start():
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe fmri = os.environ['SMF_FMRI']
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe # This is meant to be an on-demand service.
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe # Determine if nova-compute requested enablement of this instance.
367cefc17f8dcfe65651c9c16cb3151589c6cecetrawick # Exit with SMF_EXIT_TEMP_DISABLE if not true.
367cefc17f8dcfe65651c9c16cb3151589c6cecetrawick cmd = [SVCPROP, '-p', 'vnc/nova-enabled', fmri]
47d4dfaca60aff6d3c7e591bf593b3961cafcdefminfrin svcprop = subprocess.Popen(cmd, stdout=subprocess.PIPE,
47d4dfaca60aff6d3c7e591bf593b3961cafcdefminfrin stderr=subprocess.PIPE)
47d4dfaca60aff6d3c7e591bf593b3961cafcdefminfrin out, err = svcprop.communicate()
c206205e2475a7a4a192eaa7190a9894f01f0631minfrin retcode = svcprop.wait()
c206205e2475a7a4a192eaa7190a9894f01f0631minfrin if retcode != 0:
c206205e2475a7a4a192eaa7190a9894f01f0631minfrin print "Error reading 'vnc/nova-enabled' property: " + err
c206205e2475a7a4a192eaa7190a9894f01f0631minfrin return smf_include.SMF_EXIT_ERR_FATAL
304aee4b1ff85cc876570493e4ed334d42b4d9eftrawick enabled = out.strip() == 'true'
304aee4b1ff85cc876570493e4ed334d42b4d9eftrawick if not enabled:
304aee4b1ff85cc876570493e4ed334d42b4d9eftrawick smf_include.smf_method_exit(
304aee4b1ff85cc876570493e4ed334d42b4d9eftrawick smf_include.SMF_EXIT_TEMP_DISABLE,
304aee4b1ff85cc876570493e4ed334d42b4d9eftrawick "nova_enabled",
304aee4b1ff85cc876570493e4ed334d42b4d9eftrawick "nova-compute starts this service on demand")
77582a85f880a10e8e225ecd5b303446d23d1c9atrawick
77582a85f880a10e8e225ecd5b303446d23d1c9atrawick check_vncserver()
77582a85f880a10e8e225ecd5b303446d23d1c9atrawick homedir = os.environ.get('HOME')
77582a85f880a10e8e225ecd5b303446d23d1c9atrawick if not homedir:
77582a85f880a10e8e225ecd5b303446d23d1c9atrawick homedir = pwd.getpwuid(os.getuid()).pw_dir
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe os.putenv("HOME", homedir)
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe set_xstartup(homedir)
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe display = None
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe vncport = None
111b2312c9749936ebca4f273db445820a0a703ebrianp
111b2312c9749936ebca4f273db445820a0a703ebrianp try:
111b2312c9749936ebca4f273db445820a0a703ebrianp zonename = fmri.rsplit(':', 1)[1]
ad877cddc14be8c8171938ba61338c6c7b896bbdtrawick os.putenv("ZONENAME", zonename)
ad877cddc14be8c8171938ba61338c6c7b896bbdtrawick desktop_name = zonename + ' console'
ad877cddc14be8c8171938ba61338c6c7b896bbdtrawick novacfg = ConfigParser.RawConfigParser()
ad877cddc14be8c8171938ba61338c6c7b896bbdtrawick novacfg.readfp(open(NOVACFG))
ad877cddc14be8c8171938ba61338c6c7b896bbdtrawick try:
ad877cddc14be8c8171938ba61338c6c7b896bbdtrawick vnc_listenip = novacfg.get("DEFAULT", "vncserver_listen")
367cefc17f8dcfe65651c9c16cb3151589c6cecetrawick except ConfigParser.NoOptionError:
367cefc17f8dcfe65651c9c16cb3151589c6cecetrawick vnc_listenip = CONF.vncserver_listen
367cefc17f8dcfe65651c9c16cb3151589c6cecetrawick
367cefc17f8dcfe65651c9c16cb3151589c6cecetrawick with lock_available_port(vnc_listenip, VNCPORT_START, VNCPORT_END,
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe homedir) as n:
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe # NOTE: 'geometry' is that which matches the size of standard
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe # 80 character undecorated xterm window using font style specified
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe # in XTERMOPTS. The geometry doesn't matter too much because the
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe # display will be resized using xrandr once the xterm geometry is
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe # established.
f49cf8ce86a01c90d5d843fc27e19d2802dd0f77wrowe display = ":%d" % n
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe cmd = [VNCSERVER, display, "-name", desktop_name,
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe "-SecurityTypes=None", "-geometry", "964x580",
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe "-interface", vnc_listenip, "-autokill"]
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe vncport = VNCPORT_START + n
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe x11port = X11PORT_START + n
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe print "Using VNC server port: " + str(vncport)
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe print "Using X11 server port: %d, display %s" % (x11port, display)
52435ceaabd1670b2c3a062acc191159a64fb7a1wrowe vnc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
e199d79647c689a85951f19b08a08082263f4df8brianp stderr=subprocess.PIPE, env=None)
e199d79647c689a85951f19b08a08082263f4df8brianp out, err = vnc.communicate()
e199d79647c689a85951f19b08a08082263f4df8brianp vncret = vnc.wait()
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe if vncret != 0:
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe print "Error starting VNC server: " + err
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe return smf_include.SMF_EXIT_ERR_FATAL
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe except Exception as e:
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe print e
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe return smf_include.SMF_EXIT_ERR_FATAL
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe # set SMF instance port num prop
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe cmd = [SVCCFG, '-s', fmri, 'setprop', 'vnc/port', '=', 'integer:',
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe str(vncport)]
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe svccfg = subprocess.Popen(cmd, stdout=subprocess.PIPE,
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe stderr=subprocess.PIPE)
ad451e2e428a069086d1c18c9e3372f8846ec617wrowe out, err = svccfg.communicate()
1e1e5c477f92840ffbcb8acd0003305022e5468atrawick retcode = svccfg.wait()
1e1e5c477f92840ffbcb8acd0003305022e5468atrawick if retcode != 0:
1e1e5c477f92840ffbcb8acd0003305022e5468atrawick print "Error updating 'vnc/port' property: " + err
1e1e5c477f92840ffbcb8acd0003305022e5468atrawick return smf_include.SMF_EXIT_ERR_FATAL
1e1e5c477f92840ffbcb8acd0003305022e5468atrawick resize_xserver(display, zonename)
1e1e5c477f92840ffbcb8acd0003305022e5468atrawick
c998c5be82bf2b41f8fc27de9376ba10651c74bcrederpj return smf_include.SMF_EXIT_OK
c998c5be82bf2b41f8fc27de9376ba10651c74bcrederpj
c998c5be82bf2b41f8fc27de9376ba10651c74bcrederpj
c998c5be82bf2b41f8fc27de9376ba10651c74bcrederpjdef stop():
c998c5be82bf2b41f8fc27de9376ba10651c74bcrederpj try:
c998c5be82bf2b41f8fc27de9376ba10651c74bcrederpj # first kill the SMF contract
58eb8d7cca552570577aa8b636349a695ff193datrawick subprocess.check_call(["/usr/bin/pkill", "-c", sys.argv[2]])
58eb8d7cca552570577aa8b636349a695ff193datrawick except subprocess.CalledProcessError as cpe:
58eb8d7cca552570577aa8b636349a695ff193datrawick # 1 is returncode if no SMF contract processes were matched,
58eb8d7cca552570577aa8b636349a695ff193datrawick # meaning they have already terminated.
ecf435f0c6379df7ed83285d5597fc9aa39c6f6dbrianp if cpe.returncode != 1:
ecf435f0c6379df7ed83285d5597fc9aa39c6f6dbrianp print "failed to kill the SMF contract: %s" % cpe
ecf435f0c6379df7ed83285d5597fc9aa39c6f6dbrianp return smf_include.SMF_EXIT_ERR_FATAL
ecf435f0c6379df7ed83285d5597fc9aa39c6f6dbrianp
480f2a1b2fb27a8284e66e60a5bbaee6bc1ccb04trawick try:
480f2a1b2fb27a8284e66e60a5bbaee6bc1ccb04trawick fmri = os.environ['SMF_FMRI']
480f2a1b2fb27a8284e66e60a5bbaee6bc1ccb04trawick # reset port num prop to initial zero value
480f2a1b2fb27a8284e66e60a5bbaee6bc1ccb04trawick cmd = [SVCCFG, '-s', fmri, 'setprop', 'vnc/port', '=', 'integer:',
acc9093ae1f3c97acc635bd5b2c7c0969da21183trawick '0']
acc9093ae1f3c97acc635bd5b2c7c0969da21183trawick svccfg = subprocess.Popen(cmd, stdout=subprocess.PIPE,
acc9093ae1f3c97acc635bd5b2c7c0969da21183trawick stderr=subprocess.PIPE,)
2fa5f4c38890220c6ea439317e7dcb9e8b3c76f7jwoolley out, err = svccfg.communicate()
2fa5f4c38890220c6ea439317e7dcb9e8b3c76f7jwoolley retcode = svccfg.wait()
2fa5f4c38890220c6ea439317e7dcb9e8b3c76f7jwoolley if retcode != 0:
95d00ea81131488769296fa5765ed745cbf45207trawick print "Error resetting 'vnc/port' property: " + err
95d00ea81131488769296fa5765ed745cbf45207trawick return smf_include.SMF_EXIT_ERR_FATAL
95d00ea81131488769296fa5765ed745cbf45207trawick except Exception as e:
95d00ea81131488769296fa5765ed745cbf45207trawick print e
95d00ea81131488769296fa5765ed745cbf45207trawick return smf_include.SMF_EXIT_ERR_FATAL
95d00ea81131488769296fa5765ed745cbf45207trawick
95d00ea81131488769296fa5765ed745cbf45207trawick
95d00ea81131488769296fa5765ed745cbf45207trawickdef check_vncserver():
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj if not os.path.exists(VNCSERVER):
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj print("VNC console service not available on this compute node. "
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj "%s is missing. Run 'pkg install x11/server/xvnc'"
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj % VNCSERVER)
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj return smf_include.SMF_EXIT_ERR_FATAL
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj if not os.path.exists(XTERM):
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj print("VNC console service not available on this compute node. "
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj "%s is missing. Run 'pkg install terminal/xterm'"
f08574f1098defdf1dc7e7f18a1e3664ee157150rederpj % XTERM)
84854ca5d35fb9f101da948858097c88457eece8coar return smf_include.SMF_EXIT_ERR_FATAL
84854ca5d35fb9f101da948858097c88457eece8coar
84854ca5d35fb9f101da948858097c88457eece8coar
84854ca5d35fb9f101da948858097c88457eece8coardef set_xstartup(homedir):
30990c446eca5b0d16d42171a6b30da9456ff6b4trawick vncdir = os.path.join(homedir, '.vnc')
30990c446eca5b0d16d42171a6b30da9456ff6b4trawick xstartup_path = os.path.join(vncdir, 'xstartup')
30990c446eca5b0d16d42171a6b30da9456ff6b4trawick
0fd9de72e2a1be5a6134ee70703324be80d816b7trawick try:
0fd9de72e2a1be5a6134ee70703324be80d816b7trawick os.mkdir(vncdir)
0fd9de72e2a1be5a6134ee70703324be80d816b7trawick except OSError as ose:
0fd9de72e2a1be5a6134ee70703324be80d816b7trawick if ose.errno != errno.EEXIST:
2213cc395cb461faf7bfeb187ebb61d97cd457efjerenkrantz raise
f36d2c405b5a9bcc22c67577995560e7d1b616c0aaron
2213cc395cb461faf7bfeb187ebb61d97cd457efjerenkrantz # Always clobber xstartup
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe # stemp tuple = [fd, path]
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe stemp = tempfile.mkstemp(dir=vncdir)
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe os.write(stemp[0], XSTARTUP)
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe os.close(stemp[0])
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe os.chmod(stemp[1], 0700)
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe os.rename(stemp[1], xstartup_path)
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowedef resize_xserver(display, zonename):
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe """ Try to determine xterm window geometry and resize the Xvnc display
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe to match using XRANDR. Treat failure as non-fatal since an
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe incorrectly sized console is arguably better than none.
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe """
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe class UninitializedWindowError(Exception):
854c7bc4128fa2ad9fdfe0fc307d5ef30bcb5bb9wrowe pass
75f8e1cae5ca3a16a7400cdddf604815ab06b5a8rederpj
75f8e1cae5ca3a16a7400cdddf604815ab06b5a8rederpj class UnmappedWindowError(Exception):
75f8e1cae5ca3a16a7400cdddf604815ab06b5a8rederpj pass
75f8e1cae5ca3a16a7400cdddf604815ab06b5a8rederpj
75f8e1cae5ca3a16a7400cdddf604815ab06b5a8rederpj def _get_window_geometry(display, windowname):
75f8e1cae5ca3a16a7400cdddf604815ab06b5a8rederpj """ Find the xterm xwindow by name/title and extract its geometry
75f8e1cae5ca3a16a7400cdddf604815ab06b5a8rederpj Returns: tuple of window [width, height]
7f481efe04fdc4da7a447c14be62c155cbe00ddbbrianp Raises:
7f481efe04fdc4da7a447c14be62c155cbe00ddbbrianp UninitializedWindowError if window not yet initialized
7f481efe04fdc4da7a447c14be62c155cbe00ddbbrianp UnmappedWindowError if window is not viewable/mapped
7f481efe04fdc4da7a447c14be62c155cbe00ddbbrianp """
9ed34e5219ab3506ccfd2ca58751ce4c81b263a8rederpj cmd = [XWININFO, '-d', display, '-name', windowname]
9ed34e5219ab3506ccfd2ca58751ce4c81b263a8rederpj xwininfo = subprocess.Popen(cmd, stdout=subprocess.PIPE,
9ed34e5219ab3506ccfd2ca58751ce4c81b263a8rederpj stderr=subprocess.PIPE)
9ed34e5219ab3506ccfd2ca58751ce4c81b263a8rederpj out, err = xwininfo.communicate()
23b36269d124e7a6aaa5221891f7ae2ef3eeb158jerenkrantz retcode = xwininfo.wait()
23b36269d124e7a6aaa5221891f7ae2ef3eeb158jerenkrantz if retcode != 0:
23b36269d124e7a6aaa5221891f7ae2ef3eeb158jerenkrantz print "Error finding console xwindow info: " + err
d401ff3af66624a7023460054519070a025d31cfwrowe raise UninitializedWindowError
d401ff3af66624a7023460054519070a025d31cfwrowe
d401ff3af66624a7023460054519070a025d31cfwrowe width = None
d401ff3af66624a7023460054519070a025d31cfwrowe height = None
e65b56dc229f063425fac589002e34c8246ad878trawick mapped = False
e65b56dc229f063425fac589002e34c8246ad878trawick for line in out.splitlines():
e65b56dc229f063425fac589002e34c8246ad878trawick line = line.strip()
e65b56dc229f063425fac589002e34c8246ad878trawick if line.startswith("Map State:"):
306bd64cf6568149964abdf8ca748a617ed98500gregames if line.split()[-1] != "IsViewable":
306bd64cf6568149964abdf8ca748a617ed98500gregames # Window is not mapped yet.
306bd64cf6568149964abdf8ca748a617ed98500gregames raise UnmappedWindowError
5bd562b1d7da51cb5715899d32bb4c79c54459b0wrowe else:
ae3d212043d50288748fe9fdf0aa1a3e8f2ff3a6wrowe mapped = True
ae3d212043d50288748fe9fdf0aa1a3e8f2ff3a6wrowe if line.startswith("Width:"):
ae3d212043d50288748fe9fdf0aa1a3e8f2ff3a6wrowe width = int(line.split()[1])
ae3d212043d50288748fe9fdf0aa1a3e8f2ff3a6wrowe elif line.startswith("Height:"):
ae3d212043d50288748fe9fdf0aa1a3e8f2ff3a6wrowe height = int(line.split()[1])
766c20b0366e1d0e359e0d9a834669e19a4db3d9trawick if width and height and mapped:
766c20b0366e1d0e359e0d9a834669e19a4db3d9trawick return [width, height]
766c20b0366e1d0e359e0d9a834669e19a4db3d9trawick else:
766c20b0366e1d0e359e0d9a834669e19a4db3d9trawick # What, no width and height???
766c20b0366e1d0e359e0d9a834669e19a4db3d9trawick print "No window geometry info returned by " + XWINFINFO
766c20b0366e1d0e359e0d9a834669e19a4db3d9trawick raise UnmappedWindowError
2a6e98ba4ffa30ded5d8831664c5cb2a170a56b6coar
2a6e98ba4ffa30ded5d8831664c5cb2a170a56b6coar retries = 10
2a6e98ba4ffa30ded5d8831664c5cb2a170a56b6coar sleep = 1
2a6e98ba4ffa30ded5d8831664c5cb2a170a56b6coar uninit_count = 0
9a11fa4e07f50f2e5750d078ef3751ddbf441b8ftrawick unmap_count = 0
9a11fa4e07f50f2e5750d078ef3751ddbf441b8ftrawick width = 0
9a11fa4e07f50f2e5750d078ef3751ddbf441b8ftrawick height = 0
1f279dc92a60df9f61bf58468162aab0eef072e4brianp while uninit_count < retries and unmap_count < retries:
1f279dc92a60df9f61bf58468162aab0eef072e4brianp try:
1f279dc92a60df9f61bf58468162aab0eef072e4brianp width, height = _get_window_geometry(display,
1f279dc92a60df9f61bf58468162aab0eef072e4brianp 'Zone Console: ' + zonename)
2fd0edbd8b2f47a8458322bedd3b82f825faf336trawick print "Discovered xterm geometry: %d x %d" % (width, height)
2fd0edbd8b2f47a8458322bedd3b82f825faf336trawick break
2fd0edbd8b2f47a8458322bedd3b82f825faf336trawick except UninitializedWindowError:
39021cf8b495cdb94013ca73531ccb32658fb793rederpj if uninit_count < retries:
39021cf8b495cdb94013ca73531ccb32658fb793rederpj print "xterm window not initialized yet. Retrying in %ds" \
39021cf8b495cdb94013ca73531ccb32658fb793rederpj % sleep
39021cf8b495cdb94013ca73531ccb32658fb793rederpj uninit_count += 1
39021cf8b495cdb94013ca73531ccb32658fb793rederpj time.sleep(sleep)
39021cf8b495cdb94013ca73531ccb32658fb793rederpj continue
39021cf8b495cdb94013ca73531ccb32658fb793rederpj else:
39021cf8b495cdb94013ca73531ccb32658fb793rederpj print "xterm window is taking too long to initialize"
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe break
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe except UnmappedWindowError:
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe if unmap_count < retries:
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe print "Discovered xterm not mapped yet. Retrying in %ds" \
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe % sleep
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe unmap_count += 1
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe time.sleep(sleep)
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe continue
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe else:
fa16f10bc02e46bc5a6c2c2c6371926cd1dbe2edwrowe print "Discovered xterm window is taking too long to map"
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim break
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim else:
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim print "Too many failed attempts to discover xterm window geometry"
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim return
db8ac7cbb1fa6cdd6abcc4bb797d4deed32dd269jim
7b979864a91b52ecebca11d0a9a22e09349e59baminfrin # Generate a mode line for width and height, with a refresh of 60.0Hz
8d755accbdc5ae15bb0d00169b815d264c7de745minfrin cmd = [GTF, str(width), str(height), '60.0', '-x']
8d755accbdc5ae15bb0d00169b815d264c7de745minfrin gtf = subprocess.Popen(cmd, stdout=subprocess.PIPE,
7b979864a91b52ecebca11d0a9a22e09349e59baminfrin stderr=subprocess.PIPE)
7b979864a91b52ecebca11d0a9a22e09349e59baminfrin out, err = gtf.communicate()
7b0a3bcc0e689305df49f7d4da7abc35aa891862brianp retcode = gtf.wait()
7b0a3bcc0e689305df49f7d4da7abc35aa891862brianp if retcode != 0:
7b0a3bcc0e689305df49f7d4da7abc35aa891862brianp print "Error creating new modeline for VNC display: " + err
1bae4591a85d90325ecdacedf7e54d1bbfe31037aaron return
1bae4591a85d90325ecdacedf7e54d1bbfe31037aaron
1bae4591a85d90325ecdacedf7e54d1bbfe31037aaron for line in out.splitlines():
a6f48cc01ab8f5377e570c61826dcdfc36741936trawick line = line.strip()
a6f48cc01ab8f5377e570c61826dcdfc36741936trawick if line.startswith('Modeline'):
a6f48cc01ab8f5377e570c61826dcdfc36741936trawick modeline = line.split('Modeline')[1]
a6f48cc01ab8f5377e570c61826dcdfc36741936trawick print "New optimal modeline for Xvnc server: " + modeline
2da345202997f8f5860c801d68f7913c02fc05fctrawick mode = modeline.split()
2da345202997f8f5860c801d68f7913c02fc05fctrawick break
2da345202997f8f5860c801d68f7913c02fc05fctrawick
5bd562b1d7da51cb5715899d32bb4c79c54459b0wrowe # Create a new mode for the Xvnc server using the modeline generated by gtf
5bd562b1d7da51cb5715899d32bb4c79c54459b0wrowe cmd = [XRANDR, '-d', display, '--newmode']
5bd562b1d7da51cb5715899d32bb4c79c54459b0wrowe cmd.extend(mode)
1c06e98017400874d5ff6ad79f13145ec4589225striker newmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
1c06e98017400874d5ff6ad79f13145ec4589225striker stderr=subprocess.PIPE)
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe out, err = newmode.communicate()
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe retcode = newmode.wait()
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe if retcode != 0:
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe print "Error creating new xrandr modeline for VNC display: " + err
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe return
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe # Add the new mode to the default display output
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe modename = mode[0]
99a041314eb3db0b0cca325c9c40d0a6c5fdf04fwrowe cmd = [XRANDR, '-d', display, '--addmode', 'default', modename]
6e119e632566d69798ce6cf4e714ed374b72914frederpj addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
6e119e632566d69798ce6cf4e714ed374b72914frederpj stderr=subprocess.PIPE)
6e119e632566d69798ce6cf4e714ed374b72914frederpj out, err = addmode.communicate()
6e119e632566d69798ce6cf4e714ed374b72914frederpj retcode = addmode.wait()
6e119e632566d69798ce6cf4e714ed374b72914frederpj if retcode != 0:
bd120542ebe7e09cdbada5daf4924f4690e5ece3trawick print "Error adding new xrandr modeline for VNC display: " + err
bd120542ebe7e09cdbada5daf4924f4690e5ece3trawick return
bd120542ebe7e09cdbada5daf4924f4690e5ece3trawick
bd120542ebe7e09cdbada5daf4924f4690e5ece3trawick # Activate the new mode on the default display output
bd120542ebe7e09cdbada5daf4924f4690e5ece3trawick cmd = [XRANDR, '-d', display, '--output', 'default', '--mode', modename]
68d439bc0482b2e41053480f748edc2574c2ea7btrawick addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE,
68d439bc0482b2e41053480f748edc2574c2ea7btrawick stderr=subprocess.PIPE)
68d439bc0482b2e41053480f748edc2574c2ea7btrawick out, err = addmode.communicate()
68d439bc0482b2e41053480f748edc2574c2ea7btrawick retcode = addmode.wait()
dddbde8480d265d06c84f2281f01e00f8ef52e94mjc if retcode != 0:
dddbde8480d265d06c84f2281f01e00f8ef52e94mjc print "Error setting new xrandr modeline for VNC display: " + err
dddbde8480d265d06c84f2281f01e00f8ef52e94mjc return
a5ca705e053a6c754c5958aafcd6f0aa60a2e67frbb
e06675c51d084791089d79c3ac18aeae8dd465fcrbb
e06675c51d084791089d79c3ac18aeae8dd465fcrbb@contextlib.contextmanager
e06675c51d084791089d79c3ac18aeae8dd465fcrbbdef lock_available_port(address, port_start, port_end, lockdir):
e06675c51d084791089d79c3ac18aeae8dd465fcrbb """Ensures instance exclusive use of VNC, X11 service ports
481c1206b6065a8f37ab75ca1fc26c947cb37852ianh and related resources.
481c1206b6065a8f37ab75ca1fc26c947cb37852ianh Generator yields an integer of the port relative to port_start to use.
481c1206b6065a8f37ab75ca1fc26c947cb37852ianh eg. 32: VNC port 5932, X11 port 6032, X11 display :32
a964f7434f5c7f512a5fa0d0178260ccb74c84berbb lockfile is port specific and prevents multiple instances from
a964f7434f5c7f512a5fa0d0178260ccb74c84berbb attempting to use the same port number during SMF start method
a964f7434f5c7f512a5fa0d0178260ccb74c84berbb execution.
a964f7434f5c7f512a5fa0d0178260ccb74c84berbb Socket binding on address:port establishes that the port is not
6a7877447bcb8e6ff848d72f82f184c404ef4c0bminfrin already in use by another Xvnc process
6a7877447bcb8e6ff848d72f82f184c404ef4c0bminfrin """
6a7877447bcb8e6ff848d72f82f184c404ef4c0bminfrin for n in range(port_end - port_start):
9335cbd541cca1ca6038af329bbd1645310aabccminfrin vncport = port_start + n
9335cbd541cca1ca6038af329bbd1645310aabccminfrin x11port = X11PORT_START + n
9335cbd541cca1ca6038af329bbd1645310aabccminfrin lockfile = os.path.join(lockdir, '.port-%d.lock' % vncport)
9335cbd541cca1ca6038af329bbd1645310aabccminfrin try:
9335cbd541cca1ca6038af329bbd1645310aabccminfrin # Acquire port file lock first to lock out other instances trying
9335cbd541cca1ca6038af329bbd1645310aabccminfrin # to come online in parallel. They will grab the next available
9335cbd541cca1ca6038af329bbd1645310aabccminfrin # port lock.
9335cbd541cca1ca6038af329bbd1645310aabccminfrin lock = open(lockfile, 'w')
e156db58351d1c040bc72430f3eb072cb6ae7107brianp fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
e156db58351d1c040bc72430f3eb072cb6ae7107brianp
e156db58351d1c040bc72430f3eb072cb6ae7107brianp try:
e156db58351d1c040bc72430f3eb072cb6ae7107brianp # Check the VNC/RFB and X11 ports.
1c06e98017400874d5ff6ad79f13145ec4589225striker for testport in [vncport, x11port]:
1c06e98017400874d5ff6ad79f13145ec4589225striker sock = socket.socket(socket.AF_INET)
1c06e98017400874d5ff6ad79f13145ec4589225striker sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
eb1349e4ab58bd2935f7054a1bfc5c86ab5a5fa3striker try:
eb1349e4ab58bd2935f7054a1bfc5c86ab5a5fa3striker sock.bind((address, testport))
6a94da925498a20a09fde0a66002607be8d83b1astriker finally:
6a94da925498a20a09fde0a66002607be8d83b1astriker sock.close()
75161f3b2029c25bdb3f8ab87b85cb1810c479eajerenkrantz
7639aa8b39e0d9dbd096f9cc3379bcd3d5e4003bstriker # Ensure the standard X11 locking files are not present
8dc5aa056a586ffa920a6ecd5c31048702371ea6brianp # /tmp/.X<n>-lock
4c9d27bfdfea41b388dc705f7cc2b49318ab5344jim # /tmp/X11-unix/X<n>
4c9d27bfdfea41b388dc705f7cc2b49318ab5344jim xfiles = ['/tmp/.X%d-lock' % n,
4c9d27bfdfea41b388dc705f7cc2b49318ab5344jim '/tmp/X11-unix/X%d' % n]
e8e8ab3cbc3d90f15eb78e094c381a6e908fd6efjerenkrantz for xfile in xfiles:
e8e8ab3cbc3d90f15eb78e094c381a6e908fd6efjerenkrantz if os.path.exists(xfile):
e8e8ab3cbc3d90f15eb78e094c381a6e908fd6efjerenkrantz print ("Warning: X11 display :{0} is taken because of "
f4c472b8dce3c2e559232dbb5b27ed2466922ea4jerenkrantz "{1}\nRemove this file if there is no X "
f4c472b8dce3c2e559232dbb5b27ed2466922ea4jerenkrantz "server on display :{0}".format(str(n), xfile))
8dc5aa056a586ffa920a6ecd5c31048702371ea6brianp raise Exception
8dc5aa056a586ffa920a6ecd5c31048702371ea6brianp
8dc5aa056a586ffa920a6ecd5c31048702371ea6brianp except (socket.error, Exception):
7e31ef4870c7ef94838585004405e8854fefcc51ianh lock.close()
7e31ef4870c7ef94838585004405e8854fefcc51ianh os.remove(lockfile)
7e31ef4870c7ef94838585004405e8854fefcc51ianh continue
7e31ef4870c7ef94838585004405e8854fefcc51ianh # Yay, we found a free VNC/X11 port pair.
7e31ef4870c7ef94838585004405e8854fefcc51ianh yield n
bd496a3a7752a55c849e62ed00cacc492d4f6d3erederpj lock.close()
bd496a3a7752a55c849e62ed00cacc492d4f6d3erederpj os.remove(lockfile)
bd496a3a7752a55c849e62ed00cacc492d4f6d3erederpj break
bd496a3a7752a55c849e62ed00cacc492d4f6d3erederpj except IOError:
bd496a3a7752a55c849e62ed00cacc492d4f6d3erederpj print "Port %d already reserved, skipping" % vncport
a8c401eadf77822e851f19c7740e7ec6dca03daastoddard
a8c401eadf77822e851f19c7740e7ec6dca03daastoddardif __name__ == "__main__":
a8c401eadf77822e851f19c7740e7ec6dca03daastoddard os.putenv("LC_ALL", "C")
a8c401eadf77822e851f19c7740e7ec6dca03daastoddard smf_include.smf_main()
93d7153aa172665f55b04463b831ad556269c3efbrianp