#
# vnc.py: VNC related installer functionality
#
# Copyright (C) 2004, 2007 Red Hat, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Author(s): Jeremy Katz
#
import os, sys, string
import time
from snack import *
from constants_text import *
from rhpl.translate import _, N_
import network
import isys
import product
import iutil
import socket
import subprocess
import logging
log = logging.getLogger("anaconda")
class VncServer:
def __init__(self, display="1", root="/", ip=None, name=None,
desktop="Desktop", password="", vncconnecthost="",
vncconnectport="", log_file="/tmp/vncserver.log",
pw_file="/tmp/vncpassword", pw_init_file="/tmp/vncpassword.dat"):
self.display = display
self.root = root
self.ip = ip
self.name = name
self.desktop = desktop
self.password = password
self.vncconnecthost = vncconnecthost
self.vncconnectport = vncconnectport
self.log_file = log_file
self.pw_file = pw_file
self.pw_init_file = pw_init_file
self.connxinfo = None
self.log = logging.getLogger("anaconda.stdout")
def recoverVNCPassword(self):
"""Rescue the vncpassword from where loader left it
We are not to check for validity yet, if there is a file
pass it to the variable, if there is not, set the var
to ''. We will check valididty later.
"""
# see if there is a vnc password file
try:
pfile = open(self.pw_init_file, "r")
self.password=pfile.readline().strip()
pfile.close()
os.unlink(self.pw_init_file)
except:
self.password=""
def setVNCPassword(self):
"""Change the vnc server password. Output to file. """
if len(self.password) == 0:
self.setVNCParam("SecurityTypes", "None")
self.setVNCParam("rfbauth","0")
return
# If there is a password the SecurityTypes = VncAuth for all connections.
self.setVNCParam("SecurityTypes", "VncAuth")
self.setVNCParam("rfbauth",self.pw_file)
# password input combination.
pwinput = "%s\n%s\n" % (self.password, self.password)
vnccommand = [self.root+"/usr/bin/vncpasswd", self.pw_file]
vncpswdo = subprocess.Popen(vnccommand, stdin=subprocess.PIPE, stdout=subprocess.PIPE)# We pipe the output
# so the user does not see it.
(out, err) = vncpswdo.communicate(input=pwinput)
return vncpswdo.returncode
def initialize(self):
"""Here is were all the relative vars get initialized. """
# try to load /tmp/netinfo and see if we can sniff out network info
netinfo = network.Network()
# Look for the first configured interface and use its IP address for
# the computer to connect to.
dev = netinfo.getFirstDeviceName()
try:
self.ip = isys.getIPAddress(dev)
log.info("ip of %s is %s" % (dev, self.ip))
if self.ip == "127.0.0.1" or self.ip == "::1":
self.ip = None
except Exception, e:
log.warning("Got an exception trying to get the self.ip addr "
"of %s: %s" % (dev, e))
# If we have a real hostname that resolves against configured DNS
# servers, use that for the name to connect to.
if netinfo.hostname != "localhost.localdomain":
if netinfo.lookupHostname() is not None:
self.name = netinfo.hostname
elif self.ip is None:
# If we get here and there's no valid IP address, just use the
# hostname and hope for the best (better than displaying nothing)
self.name = netinfo.hostname
if self.name is not None:
self.connxinfo = "%s:%s" % (self.name, self.display)
if self.ip is not None:
try:
tmp = socket.inet_pton(socket.AF_INET6, self.ip)
family = socket.AF_INET6
except socket.error:
family = socket.AF_INET
if family == socket.AF_INET6:
ipstr = "[%s]" % self.ip
else:
ipstr = self.ip
if self.connxinfo is None:
self.connxinfo = "%s:%s" % (ipstr, self.display)
else:
self.connxinfo += " (%s)" % ipstr
# figure out product info
if self.name is not None:
self.desktop = _("%s %s installation on host %s") % (product.productName, product.productVersion, self.name)
else:
self.desktop = _("%s %s installation") % (product.productName, product.productVersion)
def setVNCParam(self, param, value):
"""Set a parameter in the Xvnc server.
Possible values for param and value. param=(values)
SecurityTypes=(VncAuth,None)
"""
vncconfigcommand = [self.root+"/usr/bin/vncconfig", "-display", ":%s"%self.display , "-set" , "%s=%s" %(param, value)]
vncconfo = subprocess.Popen(vncconfigcommand)# we dont want output
return vncconfo.returncode
def openlogfile(self):
try:
err = os.open(self.log_file, os.O_RDWR | os.O_CREAT)
if err < 0:
sys.stderr.write("error opening %s\n", log)
return None
else:
return err
except:
return None
def connectToView(self):
"""Attempt to connect to self.vncconnecthost"""
maxTries = 10
self.log.info(_("Attempting to connect to vnc client on host %s...") % (self.vncconnecthost,))
if self.vncconnectport != "":
hostarg = self.vncconnecthost + ":" + self.vncconnectport
else:
hostarg = self.vncconnecthost
vncconfigcommand = [self.root+"/usr/bin/vncconfig", "-display", ":%s"%self.display, "-connect", hostarg]
for i in range(maxTries):
vncconfp = subprocess.Popen(vncconfigcommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # vncconfig process
(out, err) = vncconfp.communicate()
if err == '':
self.log.info(_("Connected!"))
return True
elif err.startswith("connecting") and err.endswith("failed\n"):
self.log.info(_("Will try to connect again in 15 seconds..."))
time.sleep(15)
continue
else:
log.critical(err)
sys.exit(1)
self.log.error(_("Giving up attempting to connect after %d tries!\n" % maxTries ))
return False
def VNCListen(self):
"""Put the server in listening mode.
We dont really have to do anything for the server to listen :)
"""
if self.connxinfo != None:
self.log.info(_("Please manually connect your vnc client to %s to begin the install.") % (self.connxinfo,))
else:
self.log.info(_("Please manually connect your vnc client to begin the install."))
def startServer(self):
self.log.info(_("Starting VNC..."))
# Lets call it from here for now.
self.initialize()
# Lets start the xvnc regardless of vncconnecthost and password.
# We can change the configuration on the fly later.
xvnccommand = [ self.root + "/usr/bin/Xvnc", ":%s" % self.display, "-nevershared",
"-depth", "16", "-geometry", "800x600", "-br",
"IdleTimeout=0", "-auth", "/dev/null", "-once",
"DisconnectClients=false", "desktop=%s" % (self.desktop,),
"SecurityTypes=None"]
try:
xvncp = subprocess.Popen(xvnccommand, stdout=self.openlogfile(), stderr=subprocess.STDOUT)
except:
log.critical("Could not start the VNC server. Aborting.")
sys.exit(1)
# Lets give the xvnc time to initialize
time.sleep(1)
# Make sure it hasn't blown up
if xvncp.poll() != None:
sys.exit(1)
else:
self.log.info(_("The VNC server is now running."))
# Lets look at the password stuff
if self.password == "":
pass
elif len(self.password) < 6:
self.changeVNCPasswdWindow()
# Create the password file.
self.setVNCPassword()
# Lets tell the user what we are going to do.
if self.vncconnecthost != "":
self.log.warning(_("\n\nYou chose to connect to a listening vncviewer. \n"
"This does not require a password to be set. If you \n"
"set a password, it will be used in case the connection \n"
"to the vncviewer is unsuccessful\n\n"))
elif self.password == "":
self.log.warning(_("\n\nWARNING!!! VNC server running with NO PASSWORD!\n"
"You can use the self.password= boot option\n"
"if you would like to secure the server.\n\n"))
elif self.password != "":
self.log.warning(_("\n\nYou chose to execute vnc with a password. \n\n"))
else:
self.log.warning(_("\n\nUnknown Error. Aborting. \n\n"))
sys.exit(1)
# Lets try to configure the vnc server to whatever the user specified
if self.vncconnecthost != "":
connected = self.connectToView()
if not connected:
self.VNCListen()
else:
self.VNCListen()
os.environ["DISPLAY"]=":%s" % self.display
def changeVNCPasswdWindow(self):
""" Change the password to a sane parameter.
We ask user to input a password that len(password) > 6
or password == ''. Have to find a way to put askVncWindow
and this method together.
"""
screen = SnackScreen()
grid = GridFormHelp(screen, _("VNC Configuration"),"vnc", 1, 10)
bb = ButtonBar(screen, (TEXT_OK_BUTTON,
(_("No password"), "nopass")))
text = _("A password will prevent unauthorized listeners "
"connecting and monitoring your installation progress. "
"Please enter a password to be used for the installation")
grid.add(TextboxReflowed(40, text), 0, 0, (0, 0, 0, 1))
entry1 = Entry (16, password = 1)
entry2 = Entry (16, password = 1)
passgrid = Grid (2, 2)
passgrid.setField (Label (_("Password:")), 0, 0, (0, 0, 1, 0), anchorLeft = 1)
passgrid.setField (Label (_("Password (confirm):")), 0, 1, (0, 0, 1, 0), anchorLeft = 1)
passgrid.setField (entry1, 1, 0)
passgrid.setField (entry2, 1, 1)
grid.add (passgrid, 0, 1, (0, 0, 0, 1))
grid.add(bb, 0, 8, (0, 1, 1, 0), growx = 1)
while 1:
res = grid.run()
rc = bb.buttonPressed(res)
if rc == "nopass":
screen.finish()
return ""
else:
pw = entry1.value()
cf = entry2.value()
if pw != cf:
ButtonChoiceWindow(screen, _("Password Mismatch"),
_("The passwords you entered were "
"different. Please try again."),
buttons = [ TEXT_OK_BUTTON ],
width = 50)
elif len(pw) < 6:
ButtonChoiceWindow(screen, _("Password Length"),
_("The password must be at least "
"six characters long."),
buttons = [ TEXT_OK_BUTTON ],
width = 50)
else:
screen.finish()
self.password = pw
return
entry1.set("")
entry2.set("")
continue
continue
def askVncWindow():
if not os.access('/usr/bin/Xvnc', os.X_OK):
return -1
if not network.hasActiveNetDev():
return -1
screen = SnackScreen()
vncpass = None
vncconnect = 0
STEP_MESSAGE = 0
STEP_PASS = 1
STEP_DONE = 3
step = 0
while step < STEP_DONE:
if step == STEP_MESSAGE:
button = ButtonChoiceWindow(screen, _("Unable to Start X"),
_("X was unable to start on your "
"machine. Would you like to "
"start VNC to connect to "
"this computer from another "
"computer and perform a "
"graphical install or continue "
"with a text mode install?"),
buttons = [ _("Use text mode"),
_("Start VNC") ])
if button == string.lower (_("Use text mode")):
screen.finish()
return -1
else:
step = STEP_PASS
continue
if step == STEP_PASS:
grid = GridFormHelp(screen, _("VNC Configuration"),
"vnc", 1, 10)
bb = ButtonBar(screen, (TEXT_OK_BUTTON,
(_("No password"), "nopass"),
TEXT_BACK_BUTTON))
text = _("A password will prevent unauthorized listeners "
"connecting and monitoring your installation progress. "
"Please enter a password to be used for the installation")
grid.add(TextboxReflowed(40, text), 0, 0, (0, 0, 0, 1))
entry1 = Entry (16, password = 1)
entry2 = Entry (16, password = 1)
passgrid = Grid (2, 2)
passgrid.setField (Label (_("Password:")), 0, 0, (0, 0, 1, 0), anchorLeft = 1)
passgrid.setField (Label (_("Password (confirm):")), 0, 1, (0, 0, 1, 0), anchorLeft = 1)
passgrid.setField (entry1, 1, 0)
passgrid.setField (entry2, 1, 1)
grid.add (passgrid, 0, 1, (0, 0, 0, 1))
grid.add(bb, 0, 8, (0, 1, 1, 0), growx = 1)
while 1:
res = grid.run()
rc = bb.buttonPressed(res)
if rc == TEXT_BACK_CHECK:
screen.popWindow()
step = STEP_MESSAGE
break
elif rc == "nopass":
screen.finish()
return None
else:
pw = entry1.value()
cf = entry2.value()
if pw != cf:
ButtonChoiceWindow(screen, _("Password Mismatch"),
_("The passwords you entered were "
"different. Please try again."),
buttons = [ TEXT_OK_BUTTON ],
width = 50)
elif len(pw) < 6:
ButtonChoiceWindow(screen, _("Password Length"),
_("The password must be at least "
"six characters long."),
buttons = [ TEXT_OK_BUTTON ],
width = 50)
else:
screen.finish()
return pw
entry1.set("")
entry2.set("")
continue
continue
screen.finish()
return -1
if __name__ == "__main__":
askVncWindow()