994 lines
36 KiB
Python
Executable File
994 lines
36 KiB
Python
Executable File
#!/usr/bin/python
|
|
#
|
|
# anaconda: The Red Hat Linux Installation program
|
|
#
|
|
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 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 <http://www.gnu.org/licenses/>.
|
|
#
|
|
# Author(s): Brent Fox <bfox@redhat.com>
|
|
# Mike Fulbright <msf@redhat.com>
|
|
# Jakub Jelinek <jakub@redhat.com>
|
|
# Jeremy Katz <katzj@redhat.com>
|
|
# Chris Lumens <clumens@redhat.com>
|
|
# Paul Nasrat <pnasrat@redhat.com>
|
|
# Erik Troan <ewt@rpath.com>
|
|
# Matt Wilson <msw@rpath.com>
|
|
#
|
|
|
|
# This toplevel file is a little messy at the moment...
|
|
|
|
import atexit, sys, os, re, time, subprocess
|
|
from tempfile import mkstemp
|
|
|
|
# keep up with process ID of the window manager if we start it
|
|
wm_pid = None
|
|
|
|
def exitHandler(rebootData, storage, exitCode=None):
|
|
from pyanaconda import isys
|
|
from pyanaconda.flags import flags
|
|
|
|
if exitCode:
|
|
anaconda.intf.shutdown()
|
|
|
|
if "nokill" in flags.cmdline:
|
|
iutil.vtActivate(1)
|
|
print "anaconda halting due to nokill flag."
|
|
print "The system will be rebooted when you press Ctrl-Alt-Delete."
|
|
while True:
|
|
time.sleep(10000)
|
|
|
|
if image_count:
|
|
anaconda.storage.umountFilesystems(ignoreErrors=True, swapoff=False)
|
|
devicetree = anaconda.storage.devicetree
|
|
devicetree.teardownAll()
|
|
for name in devicetree.diskImages:
|
|
device = devicetree.getDeviceByName(name)
|
|
for loop in device.parents:
|
|
loop.controllable = True
|
|
device.deactivate(recursive=True)
|
|
|
|
if not flags.imageInstall and not flags.livecdInstall:
|
|
from pykickstart.constants import KS_SHUTDOWN, KS_WAIT, KS_REBOOT
|
|
from pyanaconda.iutil import dracut_eject
|
|
|
|
if rebootData.eject:
|
|
for drive in storage.devicetree.devices:
|
|
if drive.type != "cdrom":
|
|
continue
|
|
dracut_eject(drive.path)
|
|
|
|
if rebootData.action == KS_SHUTDOWN:
|
|
os.system("systemctl --no-wall poweroff")
|
|
elif rebootData.action == KS_WAIT:
|
|
os.system("systemctl --no-wall halt")
|
|
else: # reboot action is KS_REBOOT or None
|
|
os.system("systemctl --no-wall reboot")
|
|
|
|
def startMetacityWM():
|
|
childpid = os.fork()
|
|
if not childpid:
|
|
# after this point the method should never return (or throw an exception
|
|
# outside)
|
|
try:
|
|
args = ['--display', ':1',
|
|
'--sm-disable']
|
|
iutil.execWithRedirect('metacity', args,
|
|
stdout='/dev/null', stderr='/dev/null')
|
|
except BaseException as e:
|
|
# catch all possible exceptions
|
|
log.error("Problems running the window manager: %s" % str(e))
|
|
sys.exit(1)
|
|
|
|
log.info("The window manager has terminated.")
|
|
sys.exit(0)
|
|
return childpid
|
|
|
|
def startAuditDaemon():
|
|
childpid = os.fork()
|
|
if not childpid:
|
|
cmd = '/sbin/auditd'
|
|
try:
|
|
os.execl(cmd, cmd)
|
|
except OSError as e:
|
|
log.error("Error running the audit daemon: %s" % str(e))
|
|
sys.exit(0)
|
|
# auditd will turn into a daemon so catch the immediate child pid now:
|
|
os.waitpid(childpid, 0)
|
|
|
|
# function to handle X startup special issues for anaconda
|
|
def doStartupX11Actions():
|
|
"""Start window manager"""
|
|
|
|
global wm_pid # pid of the anaconda fork where the window manager is running
|
|
|
|
# now start up the window manager
|
|
wm_pid = startMetacityWM()
|
|
log.info("Starting window manager, pid %s." % (wm_pid,))
|
|
|
|
def set_x_resolution(runres):
|
|
# cant do this if no window manager is running because otherwise when we
|
|
# open and close an X connection in the xutils calls the X server will exit
|
|
# since this is the first X connection (if no window manager is running)
|
|
if runres and opts.display_mode == 'g' and not flags.usevnc and wm_pid :
|
|
try:
|
|
log.info("Setting the screen resolution to: %s.", runres)
|
|
iutil.execWithRedirect("xrandr",
|
|
["-d", ":1", "-s", runres],
|
|
stdout="/dev/tty5", stderr="/dev/tty5")
|
|
except RuntimeError as e:
|
|
log.error("The X resolution not set")
|
|
iutil.execWithRedirect("xrandr",
|
|
["-d", ":1", "-q"],
|
|
stdout="/dev/tty5", stderr="/dev/tty5")
|
|
|
|
def setupPythonUpdates():
|
|
from distutils.sysconfig import get_python_lib
|
|
|
|
# Temporary hack for F18 alpha to symlink updates and product directories
|
|
# into tmpfs. To be removed after beta in order to directly use content
|
|
# from /run/install/ -- JLK
|
|
for dirname in ("updates", "product"):
|
|
if os.path.exists("/run/install/%s" % dirname):
|
|
if os.path.islink("/tmp/%s" % dirname):
|
|
# Assume updates have already been setup
|
|
return
|
|
os.symlink("/run/install/%s" % dirname,
|
|
"/tmp/%s" % dirname)
|
|
|
|
if not os.path.exists("/tmp/updates"):
|
|
return
|
|
|
|
for pkg in os.listdir("/tmp/updates"):
|
|
d = "/tmp/updates/%s" % pkg
|
|
|
|
if not os.path.isdir(d):
|
|
continue
|
|
|
|
# See if the package exists in /usr/lib{64,}/python/?.?/site-packages.
|
|
# If it does, we can set it up as an update. If not, the pkg is
|
|
# likely a completely new directory and should not be looked at.
|
|
dest = "%s/%s" % (get_python_lib(), pkg)
|
|
if not os.access(dest, os.R_OK):
|
|
dest = "%s/%s" % (get_python_lib(1), pkg)
|
|
if not os.access(dest, os.R_OK):
|
|
continue
|
|
# Symlink over everything that's in the python libdir but not in
|
|
# the updates directory.
|
|
symlink_updates(dest, d)
|
|
|
|
import glob
|
|
import shutil
|
|
for rule in glob.glob("/tmp/updates/*.rules"):
|
|
target = "/etc/udev/rules.d/" + rule.split('/')[-1]
|
|
shutil.copyfile(rule, target)
|
|
|
|
def symlink_updates(dest_dir, update_dir):
|
|
contents = os.listdir(update_dir)
|
|
|
|
for f in os.listdir(dest_dir):
|
|
dest_path = os.path.join(dest_dir, f)
|
|
update_path = os.path.join(update_dir, f)
|
|
if f in contents:
|
|
# recurse into directories, there might be files missing in updates
|
|
if os.path.isdir(dest_path) and os.path.isdir(update_path):
|
|
symlink_updates(dest_path, update_path)
|
|
else:
|
|
if f.endswith(".pyc") or f.endswith(".pyo"):
|
|
continue
|
|
os.symlink(dest_path, update_path)
|
|
|
|
def getAnacondaVersion():
|
|
# Using _isys here so we don't drag in the logging stuff, which is always
|
|
# complicated.
|
|
from pyanaconda import _isys
|
|
return _isys.getAnacondaVersion()
|
|
|
|
def parseOptions(argv=None, cmdline=None):
|
|
from pyanaconda.anaconda_optparse import AnacondaOptionParser
|
|
|
|
# NOTE: for each long option (like '--repo'), AnacondaOptionParser
|
|
# checks the boot arguments for bootarg_prefix+option ('inst.repo').
|
|
# If require_prefix is False, it also accepts the option without the
|
|
# bootarg_prefix ('repo').
|
|
# See anaconda_optparse.py and BootArgs (in flags.py) for details.
|
|
op = AnacondaOptionParser(version="%prog " + getAnacondaVersion(),
|
|
bootarg_prefix="inst.", require_prefix=False)
|
|
|
|
# NOTE: store_false options will *not* get negated when the user does
|
|
# "option=0" on the boot commandline (store_true options do, though).
|
|
# Basically, don't use store_false unless the option starts with "no".
|
|
|
|
# Interface
|
|
op.add_option("-C", "--cmdline", dest="display_mode", action="store_const", const="c",
|
|
default="g")
|
|
op.add_option("-G", "--graphical", dest="display_mode", action="store_const", const="g")
|
|
op.add_option("-T", "--text", dest="display_mode", action="store_const", const="t")
|
|
op.add_option("-S", "--script", dest="display_mode", action="store_const", const="s")
|
|
|
|
# Network
|
|
op.add_option("--noipv4", action="store_true", default=False)
|
|
op.add_option("--noipv6", action="store_true", default=False)
|
|
op.add_option("--proxy")
|
|
|
|
# Method of operation
|
|
op.add_option("--autostep", action="store_true", default=False)
|
|
op.add_option("-d", "--debug", dest="debug", action="store_true", default=False)
|
|
op.add_option("--ks", dest="ksfile", action="store_const", const="/run/install/ks.cfg")
|
|
op.add_option("--kickstart", dest="ksfile")
|
|
op.add_option("--rescue", dest="rescue", action="store_true", default=False)
|
|
op.add_option("--targetarch", "rpmarch", dest="targetArch", type="string")
|
|
op.add_option("--armplatform", dest="armPlatform", type="string")
|
|
op.add_option("--multilib", dest="multiLib", action="store_true", default=False)
|
|
|
|
op.add_option("-m", "--method", dest="method", default=None)
|
|
op.add_option("--repo", dest="method", default=None)
|
|
op.add_option("--stage2", dest="stage2", default=None)
|
|
op.add_option("--noverifyssl", action="store_true", default=False)
|
|
|
|
op.add_option("--liveinst", action="store_true", default=False)
|
|
|
|
# Display
|
|
op.add_option("--headless", dest="isHeadless", action="store_true", default=False)
|
|
op.add_option("--nofb")
|
|
op.add_option("--resolution", dest="runres", default=None)
|
|
op.add_option("--serial", action="store_true", default=False)
|
|
op.add_option("--usefbx", dest="xdriver", action="store_const", const="fbdev")
|
|
op.add_option("--virtpconsole")
|
|
op.add_option("--vnc", action="store_true", default=False)
|
|
op.add_option("--vncconnect")
|
|
op.add_option("--vncpassword", default="")
|
|
op.add_option("--xdriver", dest="xdriver", action="store", type="string", default=None)
|
|
|
|
# Language
|
|
op.add_option("--keymap")
|
|
op.add_option("--kbdtype")
|
|
op.add_option("--lang")
|
|
|
|
# Obvious
|
|
op.add_option("--loglevel")
|
|
op.add_option("--syslog")
|
|
|
|
op.add_option("--noselinux", dest="selinux", action="store_false", default=True)
|
|
op.add_option("--selinux", action="store_true")
|
|
|
|
op.add_option("--nompath", dest="mpath", action="store_false", default=True)
|
|
op.add_option("--mpath", action="store_true")
|
|
|
|
op.add_option("--nodmraid", dest="dmraid", action="store_false", default=True)
|
|
op.add_option("--dmraid", action="store_true")
|
|
|
|
op.add_option("--noibft", dest="ibft", action="store_false", default=True)
|
|
op.add_option("--ibft", action="store_true")
|
|
op.add_option("--noiscsi", dest="iscsi", action="store_false", default=False)
|
|
op.add_option("--iscsi", action="store_true")
|
|
|
|
# Miscellaneous
|
|
op.add_option("--module", action="append", default=[])
|
|
op.add_option("--nomount", dest="rescue_nomount", action="store_true", default=False)
|
|
op.add_option("--updates", dest="updateSrc", action="store", type="string")
|
|
op.add_option("--dlabel", action="store_true", default=False)
|
|
op.add_option("--image", action="append", dest="images", default=[])
|
|
op.add_option("--memcheck", action="store_true", default=True)
|
|
op.add_option("--nomemcheck", action="store_false", dest="memcheck")
|
|
op.add_option("--leavebootorder", action="store_true", default=False)
|
|
|
|
# some defaults change based on cmdline flags
|
|
if cmdline is not None:
|
|
if "console" in cmdline:
|
|
op.set_defaults(display_mode="t")
|
|
|
|
(opts, args) = op.parse_args(argv, cmdline=cmdline)
|
|
return (opts, args, op.deprecated_bootargs)
|
|
|
|
def setupPythonPath():
|
|
# First add our updates path
|
|
sys.path.insert(0, '/tmp/updates/')
|
|
sys.path.append('/usr/share/system-config-date')
|
|
|
|
def setupEnvironment():
|
|
# Silly GNOME stuff
|
|
if os.environ.has_key('HOME') and not os.environ.has_key("XAUTHORITY"):
|
|
os.environ['XAUTHORITY'] = os.environ['HOME'] + '/.Xauthority'
|
|
os.environ['HOME'] = '/tmp'
|
|
os.environ['LC_NUMERIC'] = 'C'
|
|
os.environ["GCONF_GLOBAL_LOCKS"] = "1"
|
|
|
|
# In theory, this gets rid of our LVM file descriptor warnings
|
|
os.environ["LVM_SUPPRESS_FD_WARNINGS"] = "1"
|
|
|
|
# make sure we have /sbin and /usr/sbin in our path
|
|
os.environ["PATH"] += ":/sbin:/usr/sbin"
|
|
|
|
# we can't let the LD_PRELOAD hang around because it will leak into
|
|
# rpm %post and the like. ick :/
|
|
if os.environ.has_key("LD_PRELOAD"):
|
|
del os.environ["LD_PRELOAD"]
|
|
|
|
os.environ["GLADEPATH"] = "/tmp/updates/:/tmp/updates/data/ui/:ui/:/usr/share/anaconda/ui/:/usr/share/python-meh/"
|
|
os.environ["PIXMAPPATH"] = "/tmp/updates/pixmaps/:/tmp/updates/:/tmp/product/pixmaps/:/tmp/product/:pixmaps/:/usr/share/anaconda/pixmaps/:/usr/share/pixmaps/:/usr/share/anaconda/:/usr/share/python-meh/:/usr/share/icons/Fedora/48x48/apps/"
|
|
|
|
def setupLoggingFromOpts(opts):
|
|
if opts.loglevel and anaconda_log.logLevelMap.has_key(opts.loglevel):
|
|
level = anaconda_log.logLevelMap[opts.loglevel]
|
|
anaconda_log.logger.tty_loglevel = level
|
|
anaconda_log.setHandlersLevel(log, level)
|
|
storage_log = logging.getLogger("storage")
|
|
anaconda_log.setHandlersLevel(storage_log, level)
|
|
|
|
if opts.syslog:
|
|
anaconda_log.logger.remote_syslog = opts.syslog
|
|
|
|
def gtk_warning(title, reason):
|
|
import gtk
|
|
dialog = gtk.MessageDialog(type = gtk.MESSAGE_ERROR,
|
|
buttons = gtk.BUTTONS_CLOSE,
|
|
message_format=reason)
|
|
dialog.set_title(title)
|
|
dialog.run()
|
|
dialog.destroy()
|
|
|
|
def check_memory(anaconda, opts, display_mode=None):
|
|
reason_strict = _("%s requires %s MB of memory to install, but you only have "
|
|
"%s MB on this machine.\n")
|
|
reason_graphical = _("The %s graphical installer requires %s MB of memory, but "
|
|
"you only have %s MB.")
|
|
|
|
reboot_extra = _('\n'
|
|
'Press <return> to reboot your system.\n')
|
|
livecd_title = _("Not enough RAM")
|
|
livecd_extra =_(" Try the text mode installer by running:\n\n"
|
|
"'/usr/bin/liveinst -T'\n\n from a root "
|
|
"terminal.")
|
|
nolivecd_extra = _(" Starting text mode.")
|
|
|
|
if not display_mode:
|
|
display_mode = anaconda.displayMode
|
|
|
|
reason = reason_strict
|
|
total_ram = int(isys.total_memory() / 1024)
|
|
needed_ram = int(isys.MIN_RAM / 1024)
|
|
graphical_ram = needed_ram + int(isys.GUI_INSTALL_EXTRA_RAM / 1024)
|
|
|
|
log.info("check_memory(): total:%s, needed:%s, graphical:%s" % \
|
|
(total_ram, needed_ram, graphical_ram))
|
|
|
|
if not opts.memcheck:
|
|
log.warning("CHECK_MEMORY DISABLED")
|
|
return
|
|
|
|
if needed_ram > total_ram:
|
|
from snack import SnackScreen, ButtonChoiceWindow
|
|
if opts.liveinst:
|
|
stdoutLog.warning(reason % (product.productName, needed_ram, total_ram))
|
|
gtk_warning(livecd_title, reason % (product.productName, needed_ram, total_ram))
|
|
else:
|
|
reason += reboot_extra
|
|
screen = SnackScreen()
|
|
ButtonChoiceWindow(screen, _('Fatal Error'),
|
|
reason % (product.productName, needed_ram, total_ram),
|
|
buttons = (_("OK"),))
|
|
screen.finish()
|
|
sys.exit(1)
|
|
|
|
# override display mode if machine cannot nicely run X
|
|
if display_mode not in ('t', 'c', 's') and not flags.usevnc:
|
|
needed_ram = graphical_ram
|
|
reason = reason_graphical
|
|
|
|
if needed_ram > total_ram:
|
|
if opts.liveinst:
|
|
reason += livecd_extra
|
|
stdoutLog.warning(reason % (product.productName, needed_ram, total_ram))
|
|
title = livecd_title
|
|
gtk_warning(title, reason % (product.productName, needed_ram, total_ram))
|
|
sys.exit(1)
|
|
else:
|
|
reason += nolivecd_extra
|
|
stdoutLog.warning(reason % (product.productName, needed_ram, total_ram))
|
|
anaconda.displayMode = 't'
|
|
time.sleep(2)
|
|
|
|
def startDebugger(signum, frame):
|
|
import epdb
|
|
epdb.serve(skip=1)
|
|
|
|
def setupDisplay(anaconda, opts):
|
|
from pyanaconda.ui.tui.simpleline import App
|
|
from pyanaconda.ui.tui.spokes.askvnc import AskVNCSpoke
|
|
from pykickstart.constants import DISPLAY_MODE_TEXT
|
|
from pyanaconda import network
|
|
|
|
graphical_failed = 0
|
|
vncS = vnc.VncServer() # The vnc Server object.
|
|
vncS.anaconda = anaconda
|
|
|
|
anaconda.displayMode = opts.display_mode
|
|
anaconda.isHeadless = opts.isHeadless or iutil.isS390()
|
|
|
|
if opts.vnc:
|
|
flags.usevnc = 1
|
|
anaconda.displayMode = 'g'
|
|
vncS.password = opts.vncpassword
|
|
|
|
# Only consider vncconnect when vnc is a param
|
|
if opts.vncconnect:
|
|
cargs = string.split(opts.vncconnect, ":")
|
|
vncS.vncconnecthost = cargs[0]
|
|
if len(cargs) > 1 and len(cargs[1]) > 0:
|
|
if len(cargs[1]) > 0:
|
|
vncS.vncconnectport = cargs[1]
|
|
|
|
if opts.serial:
|
|
flags.serial = True
|
|
if opts.virtpconsole:
|
|
flags.virtpconsole = opts.virtpconsole
|
|
|
|
if opts.xdriver:
|
|
anaconda.xdriver = opts.xdriver
|
|
anaconda.writeXdriver(root="/")
|
|
|
|
if anaconda.rescue:
|
|
return
|
|
|
|
if anaconda.ksdata.vnc.enabled:
|
|
flags.usevnc = 1
|
|
anaconda.displayMode = 'g'
|
|
|
|
if vncS.password == "":
|
|
vncS.password = anaconda.ksdata.vnc.password
|
|
|
|
if vncS.vncconnecthost == "":
|
|
vncS.vncconnecthost = anaconda.ksdata.vnc.host
|
|
|
|
if vncS.vncconnectport == "":
|
|
vncS.vncconnectport = anaconda.ksdata.vnc.port
|
|
|
|
# disable VNC over text question when not enough memory is available
|
|
if iutil.memInstalled() < isys.MIN_GUI_RAM:
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC question if text mode is requested and this is a ks install
|
|
if anaconda.displayMode == 't' and flags.automatedInstall:
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC question if we were explicitly asked for text in kickstart
|
|
if anaconda.ksdata.displaymode.displayMode == DISPLAY_MODE_TEXT:
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC question if we don't have network
|
|
if not network.hasActiveNetDev():
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC question if we don't have X
|
|
if not os.access('/usr/bin/Xvnc', os.X_OK):
|
|
flags.vncquestion = False
|
|
|
|
if os.environ.has_key('DISPLAY'):
|
|
flags.preexisting_x11 = True
|
|
|
|
if anaconda.displayMode == 't' and flags.vncquestion:
|
|
#we prefer vnc over text mode, so ask about that
|
|
message = _("Text mode provides a limited set of installation "
|
|
"options. It does not allow you to specify your "
|
|
"own partitioning layout or package selections. "
|
|
"Would you like to use VNC mode instead?")
|
|
|
|
app = App("VNC Question")
|
|
spoke = AskVNCSpoke(app, anaconda.ksdata, message=message)
|
|
app.schedule_screen(spoke)
|
|
app.run()
|
|
|
|
if anaconda.ksdata.vnc.enabled:
|
|
anaconda.displayMode = 'g'
|
|
flags.usevnc = 1
|
|
vncS.password = anaconda.ksdata.vnc.password
|
|
|
|
log.info("Display mode = %s" % anaconda.displayMode)
|
|
check_memory(anaconda, opts)
|
|
|
|
# Should we try to start Xorg?
|
|
want_x = anaconda.displayMode == 'g' and \
|
|
not (flags.preexisting_x11 or flags.usevnc)
|
|
|
|
# X on a headless (e.g. s390) system? Nonsense!
|
|
if want_x and anaconda.isHeadless:
|
|
stdoutLog.warning(_("DISPLAY variable not set. Starting text mode."))
|
|
anaconda.displayMode = 't'
|
|
graphical_failed = 1
|
|
time.sleep(2)
|
|
want_x = False
|
|
|
|
# Is Xorg is actually available?
|
|
if want_x and not os.access("/usr/bin/Xorg", os.X_OK):
|
|
stdoutLog.warning(_("Graphical installation is not available. "
|
|
"Starting text mode."))
|
|
time.sleep(2)
|
|
anaconda.displayMode = 't'
|
|
want_x = False
|
|
|
|
if want_x:
|
|
# The following code depends on no SIGCHLD being delivered,
|
|
# possibly only except the one from a failing X.org. Thus
|
|
# make sure before entering this section that all the other
|
|
# children of anaconda have terminated or were forked into
|
|
# an orphan (which won't deliver a SIGCHLD to mess up the
|
|
# fragile signaling below). start X with its USR1 handler
|
|
# set to ignore. this will make it send us SIGUSR1 if it
|
|
# succeeds. if it fails, catch SIGCHLD and bomb out.
|
|
def sigchld_handler(num, frame):
|
|
raise OSError(0, "SIGCHLD caught when trying to start the X server.")
|
|
|
|
def sigusr1_handler(num, frame):
|
|
log.debug("X server has signalled a successful start.")
|
|
|
|
def preexec_fn():
|
|
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
|
|
|
|
old_sigusr1 = signal.signal(signal.SIGUSR1, sigusr1_handler)
|
|
old_sigchld = signal.signal(signal.SIGCHLD, sigchld_handler)
|
|
xout = open("/dev/tty5", "w")
|
|
try:
|
|
proc = subprocess.Popen(["Xorg", "-br",
|
|
"-logfile", "/tmp/X.log",
|
|
":1", "vt6", "-s", "1440", "-ac",
|
|
"-nolisten", "tcp", "-dpi", "96",
|
|
"-noreset"],
|
|
close_fds=True,
|
|
stdout=xout, stderr=xout,
|
|
preexec_fn=preexec_fn)
|
|
|
|
signal.pause()
|
|
os.environ["DISPLAY"] = ":1"
|
|
doStartupX11Actions()
|
|
except (OSError, RuntimeError):
|
|
stdoutLog.warning("X startup failed, falling back to text mode")
|
|
anaconda.displayMode = 't'
|
|
graphical_failed = 1
|
|
time.sleep(2)
|
|
finally:
|
|
signal.signal(signal.SIGUSR1, old_sigusr1)
|
|
signal.signal(signal.SIGCHLD, old_sigchld)
|
|
|
|
set_x_resolution(opts.runres)
|
|
|
|
if anaconda.displayMode == 't' and graphical_failed and \
|
|
flags.vncquestion and not anaconda.ksdata.vnc.enabled:
|
|
app = App("VNC Question")
|
|
spoke = AskVNCSpoke(app, anaconda.ksdata)
|
|
app.schedule_screen(spoke)
|
|
app.run()
|
|
|
|
if anaconda.ksdata.vnc.enabled:
|
|
anaconda.displayMode = 'g'
|
|
flags.usevnc = 1
|
|
vncS.password = anaconda.ksdata.vnc.password
|
|
|
|
# if they want us to use VNC do that now
|
|
if anaconda.displayMode == 'g' and flags.usevnc:
|
|
vncS.startServer()
|
|
doStartupX11Actions()
|
|
|
|
# with X running we can initialize the UI interface
|
|
anaconda.initInterface()
|
|
|
|
anaconda.instClass.configure(anaconda)
|
|
|
|
def prompt_for_ssh():
|
|
# Do some work here to get the ip addr / hostname to pass
|
|
# to the user.
|
|
from pyanaconda import network
|
|
from pyanaconda import isys
|
|
import socket
|
|
import gettext
|
|
_ = lambda x: gettext.ldgettext("anaconda", x)
|
|
|
|
ip = network.getFirstRealIP()
|
|
|
|
if not ip:
|
|
stdoutLog.error("No IP addresses found, cannot continue installation.")
|
|
sys.exit(1)
|
|
|
|
ipstr = ip
|
|
|
|
try:
|
|
hinfo = socket.gethostbyaddr(ipstr)
|
|
except Exception as e:
|
|
stdoutLog.debug("Exception caught trying to get host name of %s: %s" %
|
|
(ipstr, e))
|
|
name = network.getDefaultHostname(None)
|
|
else:
|
|
if len(hinfo) == 3:
|
|
name = hinfo[0]
|
|
|
|
if ip.find(':') != -1:
|
|
ipstr = "[%s]" % (ip,)
|
|
|
|
if (name is not None) and (not name.startswith('localhost')) and (ipstr is not None):
|
|
connxinfo = "%s (%s)" % (socket.getfqdn(name=name), ipstr,)
|
|
elif ipstr is not None:
|
|
connxinfo = "%s" % (ipstr,)
|
|
else:
|
|
connxinfo = None
|
|
|
|
if connxinfo:
|
|
stdoutLog.info(_("Please ssh install@%s to begin the install.") % connxinfo)
|
|
else:
|
|
stdoutLog.info(_("Please ssh install@<host> to continue installation."))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print "Starting installer, one moment..."
|
|
|
|
# Allow a file to be loaded as early as possible
|
|
try:
|
|
import updates_disk_hook
|
|
except ImportError:
|
|
pass
|
|
|
|
# this handles setting up updates for pypackages to minimize the set needed
|
|
setupPythonUpdates()
|
|
setupPythonPath()
|
|
|
|
# do this early so we can set flags before initializing logging
|
|
from pyanaconda.flags import flags
|
|
(opts, args, depr) = parseOptions(cmdline=flags.cmdline)
|
|
if opts.images:
|
|
flags.imageInstall = True
|
|
|
|
# Set up logging as early as possible.
|
|
import logging
|
|
from pyanaconda import anaconda_log
|
|
anaconda_log.init()
|
|
anaconda_log.logger.setupVirtio()
|
|
|
|
log = logging.getLogger("anaconda")
|
|
stdoutLog = logging.getLogger("anaconda.stdout")
|
|
|
|
if os.geteuid() != 0:
|
|
stdoutLog.error("anaconda must be run as root.")
|
|
sys.exit(0)
|
|
|
|
# see if we're on s390x and if we've got an ssh connection
|
|
uname = os.uname()
|
|
if uname[4] == 's390x':
|
|
if 'TMUX' not in os.environ and "RUNKS" not in flags.cmdline:
|
|
prompt_for_ssh()
|
|
sys.exit(0)
|
|
# If we get RUNKS, we default to cmdline display mode, because nothing
|
|
# else will work. Kickstart options can still override.
|
|
if "RUNKS" in flags.cmdline:
|
|
opts.display_mode = 'c'
|
|
|
|
log.info("%s %s" % (sys.argv[0], getAnacondaVersion()))
|
|
|
|
# TODO: uncomment this when we're sure that we're doing the right thing
|
|
# with flags.cmdline *everywhere* it appears...
|
|
#for arg in depr:
|
|
# stdoutLog.warn("Boot argument '%s' is deprecated. "
|
|
# "In the future, use 'inst.%s'.", arg, arg)
|
|
|
|
# pull this in to get product name and versioning
|
|
from pyanaconda import product
|
|
from pyanaconda.constants import ROOT_PATH, DEFAULT_LANG
|
|
|
|
from pyanaconda import isys
|
|
isys.initLog()
|
|
|
|
import signal, string, time
|
|
|
|
from pyanaconda import iutil
|
|
from pyanaconda import vnc
|
|
from pyanaconda import kickstart
|
|
from pyanaconda import ntp
|
|
from pyanaconda import keyboard
|
|
|
|
verdesc = "%s for %s %s" % (getAnacondaVersion(),
|
|
product.productName, product.productVersion)
|
|
if product.isFinal:
|
|
print "anaconda %s started." % verdesc
|
|
else:
|
|
print "anaconda %s (pre-release) started." % verdesc
|
|
|
|
import gettext
|
|
_ = lambda x: gettext.ldgettext("anaconda", x)
|
|
|
|
from pyanaconda import Anaconda
|
|
anaconda = Anaconda()
|
|
iutil.setup_translations(gettext)
|
|
|
|
# reset python's default SIGINT handler
|
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
signal.signal(signal.SIGSEGV, isys.handleSegv)
|
|
|
|
setupEnvironment()
|
|
# make sure we have /var/log soon, some programs fail to start without it
|
|
iutil.mkdirChain("/var/log")
|
|
|
|
pidfile = open("/var/run/anaconda.pid", "w")
|
|
pidfile.write("%s\n" % (os.getpid(),))
|
|
del pidfile
|
|
# add our own additional signal handlers
|
|
signal.signal(signal.SIGHUP, startDebugger)
|
|
|
|
anaconda.opts = opts
|
|
|
|
# check memory, just the text mode for now:
|
|
check_memory(anaconda, opts, 't')
|
|
|
|
# Now that we've got arguments, do some extra processing.
|
|
setupLoggingFromOpts(opts)
|
|
|
|
# Default is to prompt to mount the installed system.
|
|
anaconda.rescue_mount = not opts.rescue_nomount
|
|
|
|
if opts.dlabel: #autodetected driverdisc in use
|
|
flags.dlabel = True
|
|
|
|
if opts.noipv4:
|
|
flags.useIPv4 = False
|
|
|
|
if opts.noipv6:
|
|
flags.useIPv6 = False
|
|
|
|
if opts.proxy:
|
|
anaconda.proxy = opts.proxy
|
|
|
|
if opts.updateSrc:
|
|
anaconda.updateSrc = opts.updateSrc
|
|
|
|
if opts.method:
|
|
anaconda.methodstr = opts.method
|
|
|
|
if opts.stage2:
|
|
anaconda.stage2 = opts.stage2
|
|
|
|
if opts.noverifyssl:
|
|
flags.noverifyssl = True
|
|
|
|
if opts.liveinst:
|
|
flags.livecdInstall = True
|
|
|
|
if opts.module:
|
|
for mod in opts.module:
|
|
(path, name) = string.split(mod, ":")
|
|
anaconda.extraModules.append((path, name))
|
|
|
|
if opts.ibft:
|
|
flags.ibft = 1
|
|
|
|
if opts.iscsi:
|
|
flags.iscsi = 1
|
|
|
|
if opts.targetArch:
|
|
flags.targetarch = opts.targetArch
|
|
|
|
if opts.armPlatform:
|
|
flags.armPlatform = opts.armPlatform
|
|
|
|
# set flags
|
|
flags.dmraid = opts.dmraid
|
|
flags.mpath = opts.mpath
|
|
flags.selinux = opts.selinux
|
|
|
|
if not flags.livecdInstall and not flags.imageInstall:
|
|
startAuditDaemon()
|
|
|
|
# setup links required for all install types
|
|
for i in ( "services", "protocols", "nsswitch.conf", "joe", "selinux",
|
|
"mke2fs.conf" ):
|
|
try:
|
|
if os.path.exists("/mnt/runtime/etc/" + i):
|
|
os.symlink ("../mnt/runtime/etc/" + i, "/etc/" + i)
|
|
except:
|
|
pass
|
|
|
|
if opts.debug:
|
|
flags.debug = True
|
|
|
|
if opts.rescue:
|
|
anaconda.rescue = True
|
|
|
|
log.info("anaconda called with cmdline = %s" %(sys.argv,))
|
|
log.info("Default encoding = %s " % sys.getdefaultencoding())
|
|
|
|
os.system("udevadm control --env=ANACONDA=1")
|
|
|
|
# If we were given a kickstart file on the command line, parse (but do not
|
|
# execute) that now. Otherwise, load in defaults from kickstart files
|
|
# shipped with the installation media.
|
|
ksdata = None
|
|
if opts.ksfile:
|
|
flags.automatedInstall = True
|
|
files = [opts.ksfile]
|
|
else:
|
|
files = ["/tmp/updates/interactive-defaults.ks",
|
|
"/usr/share/anaconda/interactive-defaults.ks"]
|
|
|
|
for f in files:
|
|
if not os.path.exists(f):
|
|
continue
|
|
|
|
kickstart.preScriptPass(f)
|
|
ksdata = kickstart.parseKickstart(f)
|
|
|
|
# Only load the first defaults file we find.
|
|
break
|
|
|
|
if not ksdata:
|
|
ksdata = kickstart.AnacondaKSHandler()
|
|
|
|
if ksdata.rescue.rescue:
|
|
anaconda.rescue = True
|
|
|
|
# Some kickstart commands must be executed immediately, as they affect
|
|
# how anaconda operates.
|
|
ksdata.logging.execute()
|
|
|
|
anaconda.ksdata = ksdata
|
|
|
|
# setup keyboard layout from the command line option and let
|
|
# it override from kickstart if/when X is initialized
|
|
if opts.keymap:
|
|
if not ksdata.keyboard.keyboard:
|
|
ksdata.keyboard.keyboard = opts.keymap
|
|
|
|
if ksdata.keyboard.keyboard:
|
|
keyboard.activate_keyboard(ksdata.keyboard)
|
|
|
|
# Some post-install parts of anaconda are implemented as kickstart
|
|
# scripts. Add those to the ksdata now.
|
|
kickstart.appendPostScripts(ksdata)
|
|
|
|
# cmdline flags override kickstart settings
|
|
ksdata.method.proxy = anaconda.proxy
|
|
ksdata.method.noverifyssl = flags.noverifyssl
|
|
|
|
# set ksdata.method based on anaconda.method if it isn't already set
|
|
if anaconda.methodstr and not ksdata.method.method:
|
|
if anaconda.methodstr.startswith("cdrom"):
|
|
ksdata.method.method = "cdrom"
|
|
elif anaconda.methodstr.startswith("nfs"):
|
|
ksdata.method.method = "nfs"
|
|
(options, server, path) = iutil.parseNfsUrl(anaconda.methodstr)
|
|
ksdata.method.server = server
|
|
ksdata.method.dir = path
|
|
ksdata.method.opts = options
|
|
elif anaconda.methodstr.startswith("hd:"):
|
|
ksdata.method.method = "harddrive"
|
|
url = anaconda.methodstr.split(":", 1)[1]
|
|
url_parts = url.split(":")
|
|
device = url_parts[0]
|
|
path = ""
|
|
if len(url_parts) == 2:
|
|
path = url_parts[1]
|
|
elif len(url_parts) == 3:
|
|
fstype = url_parts[1] # XXX not used
|
|
path = url_parts[2]
|
|
|
|
ksdata.method.partition = device
|
|
ksdata.method.dir = path
|
|
elif anaconda.methodstr.startswith("http") or \
|
|
anaconda.methodstr.startswith("ftp"):
|
|
ksdata.method.method = "url"
|
|
ksdata.method.url = anaconda.methodstr
|
|
elif anaconda.methodstr.startswith("livecd"):
|
|
ksdata.method.method = "livecd"
|
|
device = anaconda.methodstr.split(":", 1)[1]
|
|
ksdata.method.partition = os.path.normpath(device)
|
|
else:
|
|
log.error("Unknown method: %s", (anaconda.methodstr,))
|
|
|
|
# Set the language before loading an interface, when it may be too late.
|
|
if opts.lang:
|
|
from pyanaconda.localization import Language, LOCALE_PREFERENCES, expand_langs
|
|
|
|
langObj = Language(LOCALE_PREFERENCES, territory=None)
|
|
|
|
# Given something other than the long format we prefer? We need to
|
|
# dig through supported translations to figure out what the user
|
|
# meant.
|
|
if not opts.lang in langObj.translations:
|
|
foundLang = False
|
|
|
|
for trans in langObj.translations.keys():
|
|
if opts.lang in expand_langs(trans):
|
|
opts.lang = trans
|
|
foundLang = True
|
|
break
|
|
|
|
if not foundLang:
|
|
opts.lang = "en_US.UTF-8"
|
|
|
|
langObj.set_install_lang(opts.lang)
|
|
ksdata.lang.lang = opts.lang
|
|
|
|
# init threading before Gtk can do anything
|
|
from pyanaconda.threads import initThreading, threadMgr, AnacondaThread
|
|
initThreading()
|
|
|
|
# now start the interface
|
|
setupDisplay(anaconda, opts)
|
|
|
|
# Set flag to prompt for missing ks data
|
|
if anaconda.displayMode == 'c':
|
|
flags.ksprompt = False
|
|
|
|
image_count = 0
|
|
for image in opts.images:
|
|
image_spec = image.rsplit(":", 1)
|
|
path = image_spec[0]
|
|
if len(image_spec) == 2 and image_spec[1].strip():
|
|
name = image_spec[1].strip()
|
|
else:
|
|
name = os.path.splitext(os.path.basename(path))[0]
|
|
|
|
if "/" in name or name in anaconda.storage.config.diskImages.keys():
|
|
name = "diskimg%d" % image_count
|
|
|
|
log.info("naming disk image '%s' '%s'" % (path, name))
|
|
anaconda.storage.config.diskImages[name] = path
|
|
image_count += 1
|
|
flags.imageInstall = True
|
|
|
|
if image_count:
|
|
anaconda.storage.setupDiskImages()
|
|
anaconda.simpleFilter = True
|
|
|
|
# sets yum's multilib_policy to "all" (as opposed to "best")
|
|
ksdata.packages.multiLib = opts.multiLib
|
|
|
|
from pyanaconda import exception
|
|
if anaconda.displayMode == 'g':
|
|
# comment out the next line to make exceptions non-fatal
|
|
anaconda.mehConfig = exception.initExceptionHandling(anaconda)
|
|
|
|
# add our own additional signal handlers
|
|
signal.signal(signal.SIGUSR1, lambda signum, frame:
|
|
exception.test_exception_handling())
|
|
signal.signal(signal.SIGUSR2, lambda signum, frame: anaconda.dumpState())
|
|
|
|
if opts.lang:
|
|
# this is lame, but make things match what we expect (#443408)
|
|
opts.lang = opts.lang.replace(".utf8", ".UTF-8")
|
|
|
|
from pyanaconda.storage import storageInitialize
|
|
from pyanaconda.packaging import payloadInitialize
|
|
from pyanaconda.network import networkInitialize
|
|
|
|
if anaconda.rescue:
|
|
from pyanaconda.rescue import doRescue
|
|
doRescue(anaconda.rescue_mount, ksdata, anaconda.platform)
|
|
|
|
threadMgr.add(AnacondaThread(name="AnaStorageThread", target=storageInitialize, args=(anaconda.storage, ksdata, anaconda.protected)))
|
|
threadMgr.add(AnacondaThread(name="AnaNetworkThread", target=networkInitialize, args=(ksdata,)))
|
|
threadMgr.add(AnacondaThread(name="AnaPayloadThread", target=payloadInitialize, args=(anaconda.storage, ksdata, anaconda.payload)))
|
|
|
|
atexit.register(exitHandler, ksdata.reboot, anaconda.storage)
|
|
|
|
# setup ntp servers and start NTP daemon if not requested otherwise
|
|
if (not flags.imageInstall) and anaconda.ksdata.timezone.ntpservers:
|
|
ntp.save_servers_to_config(anaconda.ksdata.timezone.ntpservers)
|
|
|
|
if not anaconda.ksdata.timezone.nontp:
|
|
iutil.start_service("chronyd")
|
|
|
|
# FIXME: This will need to be made cleaner once this file starts to take
|
|
# shape with the new UI code.
|
|
anaconda._intf.setup(ksdata)
|
|
anaconda._intf.run()
|
|
|
|
# vim:tw=78:ts=4:et:sw=4
|