# # 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()