3e63d1dd37
Apply diff anaconda-20.25.16-1..anaconda-21.48.21-1
1407 lines
55 KiB
Python
Executable File
1407 lines
55 KiB
Python
Executable File
#!/usr/bin/python
|
|
#
|
|
# anaconda: The Red Hat Linux Installation program
|
|
#
|
|
# Copyright (C) 1999-2013
|
|
# 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... (2001-06-22)
|
|
# ...still messy (2013-07-12)
|
|
|
|
coverage = None
|
|
|
|
proc_cmdline = open("/proc/cmdline", "r").read()
|
|
proc_cmdline = proc_cmdline.split()
|
|
if ("debug=1" in proc_cmdline) or ("debug" in proc_cmdline):
|
|
import coverage
|
|
cov = coverage.coverage(data_file="/mnt/sysimage/root/anaconda.coverage",
|
|
branch=True,
|
|
source=["/usr/sbin/anaconda", "pyanaconda"]
|
|
)
|
|
cov.start()
|
|
|
|
|
|
import atexit, sys, os, time, subprocess, signal, errno
|
|
|
|
# Install a global SIGCHLD handler to keep track of things that should be
|
|
# running for as long as anaconda does. The dictionary is of the form
|
|
# {pid: name, ...}. The handler will raise ExitError (defined below), so if
|
|
# not caught a SIGCHLD from a watched process will halt anaconda.
|
|
forever_pids = {}
|
|
|
|
class ExitError(RuntimeError):
|
|
pass
|
|
|
|
def sigchld_handler(num, frame):
|
|
# Check whether anything in the list of processes being watched has
|
|
# exited. We don't want to call waitpid(-1), since that would break
|
|
# anything else using wait/waitpid (like the subprocess module).
|
|
exited_pids = []
|
|
exn_message = []
|
|
|
|
for child_pid in forever_pids:
|
|
try:
|
|
pid_result, status = iutil.eintr_retry_call(os.waitpid, child_pid, os.WNOHANG)
|
|
except OSError as e:
|
|
if e.errno == errno.ECHILD:
|
|
continue
|
|
|
|
if pid_result:
|
|
proc_name = forever_pids[child_pid]
|
|
exited_pids.append(child_pid)
|
|
|
|
if os.WIFEXITED(status):
|
|
status_str = "with status %s" % os.WEXITSTATUS(status)
|
|
elif os.WIFSIGNALED(status):
|
|
status_str = "on signal %s" % os.WTERMSIG(status)
|
|
else:
|
|
status_str = "with unknown status code %s" % status
|
|
|
|
exn_message.append("%s exited %s" % (proc_name, status_str))
|
|
|
|
for child_pid in exited_pids:
|
|
del forever_pids[child_pid]
|
|
|
|
if exn_message:
|
|
raise ExitError(", ".join(exn_message))
|
|
|
|
signal.signal(signal.SIGCHLD, sigchld_handler)
|
|
|
|
# Fork a new process and add it to forever_pids. The return values are the
|
|
# the same as os.fork, but neither the parent nor the child will return
|
|
# until the process is watched.
|
|
def start_watched_pid(proc_name):
|
|
readpipe, writepipe = os.pipe()
|
|
childpid = os.fork()
|
|
if childpid == 0:
|
|
# No need for the write pipe in the child
|
|
iutil.eintr_retry_call(os.close, writepipe)
|
|
|
|
# Wait for the parent to signal that it's ready to return
|
|
iutil.eintr_retry_call(os.read, readpipe, 1)
|
|
|
|
# Ready to go
|
|
iutil.eintr_retry_call(os.close, readpipe)
|
|
return childpid
|
|
else:
|
|
# No need for the read pipe in the parent
|
|
iutil.eintr_retry_call(os.close, readpipe)
|
|
|
|
# Add the pid to the list of watched pids
|
|
forever_pids[childpid] = proc_name
|
|
|
|
# Signal to the child that we're ready to return
|
|
# D is for Done
|
|
iutil.eintr_retry_call(os.write, writepipe, 'D')
|
|
iutil.eintr_retry_call(os.close, writepipe)
|
|
return childpid
|
|
|
|
def exitHandler(rebootData, storage, exitCode=None):
|
|
# Clear the list of watched PIDs.
|
|
global forever_pids
|
|
forever_pids = {}
|
|
|
|
# stop and save coverage here b/c later the file system may be unavailable
|
|
if coverage is not None:
|
|
cov.stop()
|
|
if os.path.isdir('/mnt/sysimage/root'):
|
|
cov.save()
|
|
|
|
if flags.usevnc:
|
|
vnc.shutdownServer()
|
|
|
|
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 or flags.dirInstall:
|
|
anaconda.storage.umountFilesystems(swapoff=False)
|
|
devicetree = anaconda.storage.devicetree
|
|
devicetree.teardownAll()
|
|
for imageName in devicetree.diskImages:
|
|
dev = devicetree.getDeviceByName(imageName)
|
|
for loop in dev.parents:
|
|
loop.controllable = True
|
|
dev.deactivate(recursive=True)
|
|
|
|
if anaconda.dbus_inhibit_id:
|
|
from pyanaconda.screensaver import uninhibit_screensaver
|
|
uninhibit_screensaver(anaconda.dbus_session_connection, anaconda.dbus_inhibit_id)
|
|
anaconda.dbus_inhibit_id = None
|
|
|
|
if not flags.imageInstall and not flags.livecdInstall \
|
|
and not flags.dirInstall:
|
|
from pykickstart.constants import KS_SHUTDOWN, KS_WAIT
|
|
from pyanaconda.iutil import dracut_eject, get_mount_paths
|
|
|
|
if flags.eject or rebootData.eject:
|
|
for cdrom in storage.devicetree.getDevicesByType("cdrom"):
|
|
if get_mount_paths(cdrom.path):
|
|
dracut_eject(cdrom.path)
|
|
|
|
if rebootData.action == KS_SHUTDOWN:
|
|
subprocess.Popen(["systemctl", "--no-wall", "poweroff"])
|
|
elif rebootData.action == KS_WAIT:
|
|
subprocess.Popen(["systemctl", "--no-wall", "halt"])
|
|
else: # reboot action is KS_REBOOT or None
|
|
subprocess.Popen(["systemctl", "--no-wall", "reboot"])
|
|
|
|
def startSpiceVDAgent():
|
|
status = iutil.execWithRedirect("spice-vdagent", [])
|
|
|
|
if status:
|
|
log.info("spice-vdagent exited with status %d", status)
|
|
else:
|
|
log.info("Started spice-vdagent.")
|
|
|
|
def startX11():
|
|
# Start X11 with its USR1 handler set to ignore, which will make it send
|
|
# us SIGUSR1 if it succeeds. If it fails, catch SIGCHLD and bomb out.
|
|
# Use a list so the value can be modified from the handler
|
|
x11_started = [False]
|
|
def sigusr1_handler(num, frame):
|
|
log.debug("X server has signalled a successful start.")
|
|
x11_started[0] = True
|
|
|
|
# Fail after, let's say a minute, in case something weird happens
|
|
# and we don't receive SIGUSR1
|
|
def sigalrm_handler(num, frame):
|
|
# Check that it didn't make it under the wire
|
|
if x11_started[0]:
|
|
return
|
|
log.error("Timeout trying to start the X server")
|
|
raise ExitError("Timeout trying to start the X server")
|
|
|
|
try:
|
|
old_sigusr1_handler = signal.signal(signal.SIGUSR1, sigusr1_handler)
|
|
old_sigalrm_handler = signal.signal(signal.SIGALRM, sigalrm_handler)
|
|
|
|
childpid = start_watched_pid("Xorg")
|
|
|
|
if not childpid:
|
|
# after this point the method should never return (or throw an exception
|
|
# outside)
|
|
try:
|
|
# dup /dev/tty5 to stdout and stderr
|
|
xfd = iutil.eintr_retry_call(os.open, "/dev/tty5", os.O_WRONLY | os.O_APPEND)
|
|
iutil.eintr_retry_call(os.dup2, xfd, sys.stdout.fileno())
|
|
iutil.eintr_retry_call(os.dup2, xfd, sys.stderr.fileno())
|
|
|
|
# Close all other file descriptors
|
|
try:
|
|
maxfd = os.sysconf("SC_OPEN_MAX")
|
|
except ValueError:
|
|
maxfd = 1024
|
|
|
|
os.closerange(3, maxfd)
|
|
|
|
# Replace the SIGUSR1 handler with SIG_IGN as described above
|
|
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
|
|
|
|
# Run it
|
|
os.execlp("Xorg", "Xorg", "-br",
|
|
"-logfile", "/tmp/X.log",
|
|
":1", "vt6", "-s", "1440", "-ac",
|
|
"-nolisten", "tcp", "-dpi", "96",
|
|
"-noreset")
|
|
|
|
# We should never get here
|
|
raise OSError(0, "Unable to exec")
|
|
except BaseException as e:
|
|
# catch all possible exceptions
|
|
# Reopen the logger in case it was closed by closerange.
|
|
# Use a different name because assigning to variables across a
|
|
# fork confuses the hell out of python.
|
|
child_log = logging.getLogger("anaconda")
|
|
child_log.error("Problems running Xorg: %s", e)
|
|
os._exit(1)
|
|
|
|
# Parent process
|
|
# Start the timer
|
|
signal.alarm(60)
|
|
|
|
# Wait for SIGUSR1
|
|
while not x11_started[0]:
|
|
signal.pause()
|
|
|
|
# Now that X is started, make $DISPLAY available
|
|
os.environ["DISPLAY"] = ":1"
|
|
finally:
|
|
# Put everything back where it was
|
|
signal.alarm(0)
|
|
signal.signal(signal.SIGUSR1, old_sigusr1_handler)
|
|
signal.signal(signal.SIGALRM, old_sigalrm_handler)
|
|
|
|
def startMetacityWM():
|
|
# When metacity actually connects to the X server is unknowable, but
|
|
# fortunately it doesn't matter. metacity does not need to be the first
|
|
# connection to Xorg, and if anaconda starts up before metacity, metacity
|
|
# will just take over and maximize the window and make everything right,
|
|
# fingers crossed.
|
|
|
|
childpid = start_watched_pid("metacity")
|
|
if not childpid:
|
|
# after this point the method should never return (or throw an exception
|
|
# outside)
|
|
try:
|
|
returncode = iutil.execWithRedirect('metacity', ["--display", ":1", "--sm-disable"])
|
|
except BaseException as e:
|
|
# catch all possible exceptions
|
|
log.error("Problems running the window manager: %s", e)
|
|
os._exit(1)
|
|
|
|
log.info("The window manager has terminated.")
|
|
os._exit(returncode)
|
|
|
|
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", e)
|
|
os._exit(0)
|
|
# auditd will turn into a daemon so catch the immediate child pid now:
|
|
iutil.eintr_retry_call(os.waitpid, childpid, 0)
|
|
|
|
# function to handle X startup special issues for anaconda
|
|
def doStartupX11Actions():
|
|
"""Start window manager"""
|
|
|
|
# now start up the window manager
|
|
wm_pid = startMetacityWM()
|
|
log.info("Starting window manager, pid %s.", wm_pid)
|
|
|
|
def set_x_resolution(runres):
|
|
if runres and opts.display_mode == 'g' and not flags.usevnc:
|
|
try:
|
|
log.info("Setting the screen resolution to: %s.", runres)
|
|
iutil.execWithRedirect("xrandr",
|
|
["-d", ":1", "-s", runres])
|
|
except RuntimeError:
|
|
log.error("The X resolution not set")
|
|
iutil.execWithRedirect("xrandr",
|
|
["-d", ":1", "-q"])
|
|
|
|
def doExtraX11Actions(runres):
|
|
"""Perform X11 actions not related to startup"""
|
|
|
|
set_x_resolution(runres)
|
|
|
|
startSpiceVDAgent()
|
|
|
|
def setupPythonUpdates():
|
|
from distutils.sysconfig import get_python_lib
|
|
import gi.overrides
|
|
|
|
# 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)
|
|
|
|
gi.overrides.__path__.insert(0, "/run/install/updates")
|
|
|
|
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 getAnacondaVersionString():
|
|
# we are importing the startup module directly so that it can be replaced
|
|
# by updates image, if it was replaced before the updates image can be
|
|
# loaded, it could not be easily replaced
|
|
from pyanaconda import startup_utils
|
|
return startup_utils.get_anaconda_version_string()
|
|
|
|
|
|
def parseArguments(argv=None, boot_cmdline=None):
|
|
from pyanaconda.anaconda_argparse import AnacondaArgumentParser
|
|
from pyanaconda.anaconda_argparse import HelpTextParser
|
|
|
|
datadir = os.environ.get("ANACONDA_DATADIR", "/usr/share/anaconda")
|
|
|
|
# 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.
|
|
ap = AnacondaArgumentParser(bootarg_prefix="inst.", require_prefix=False)
|
|
help_parser = HelpTextParser(os.path.join(datadir, "anaconda_options.txt"))
|
|
|
|
# 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".
|
|
|
|
# YET ANOTHER NOTE: If you change anything here:
|
|
# a) document its usage in docs/boot-options.txt
|
|
# b) be prepared to maintain it for a very long time
|
|
# If this seems like too much trouble, *don't add a new option*!
|
|
|
|
# Version
|
|
ap.add_argument('--version', action='version', version="%(prog)s " + getAnacondaVersionString())
|
|
|
|
# Interface
|
|
ap.add_argument("-C", "--cmdline", dest="display_mode", action="store_const", const="c",
|
|
default="g", help=help_parser.help_text("cmdline"))
|
|
ap.add_argument("-G", "--graphical", dest="display_mode", action="store_const", const="g",
|
|
help=help_parser.help_text("graphical"))
|
|
ap.add_argument("-T", "--text", dest="display_mode", action="store_const", const="t",
|
|
help=help_parser.help_text("text"))
|
|
|
|
# Network
|
|
ap.add_argument("--proxy", metavar='PROXY_URL', help=help_parser.help_text("proxy"))
|
|
|
|
# Method of operation
|
|
ap.add_argument("-d", "--debug", dest="debug", action="store_true",
|
|
default=False, help=help_parser.help_text("debug"))
|
|
ap.add_argument("--ks", dest="ksfile", action="store_const",
|
|
metavar="KICKSTART_URL", const="/run/install/ks.cfg",
|
|
help=help_parser.help_text("ks"))
|
|
ap.add_argument("--kickstart", dest="ksfile", metavar="KICKSTART_PATH",
|
|
help=help_parser.help_text("kickstart"))
|
|
ap.add_argument("--rescue", dest="rescue", action="store_true", default=False,
|
|
help=help_parser.help_text("rescue"))
|
|
ap.add_argument("--armplatform", dest="armPlatform", type=str, metavar="PLATFORM_ID",
|
|
help=help_parser.help_text("armplatform"))
|
|
ap.add_argument("--multilib", dest="multiLib", action="store_true", default=False,
|
|
help=help_parser.help_text("multilib"))
|
|
|
|
ap.add_argument("-m", "--method", dest="method", default=None, metavar="METHOD",
|
|
help=help_parser.help_text("method"))
|
|
ap.add_argument("--askmethod", dest="askmethod", action="store_true", default=False,
|
|
help=help_parser.help_text("askmethod"))
|
|
ap.add_argument("--repo", dest="method", default=None, metavar="REPO_URL",
|
|
help=help_parser.help_text("repo"))
|
|
ap.add_argument("--stage2", dest="stage2", default=None, metavar="STAGE2_URL",
|
|
help=help_parser.help_text("stage2"))
|
|
ap.add_argument("--noverifyssl", action="store_true", default=False,
|
|
help=help_parser.help_text("noverifyssl"))
|
|
ap.add_argument("--liveinst", action="store_true", default=False,
|
|
help=help_parser.help_text("liveinst"))
|
|
|
|
# Display
|
|
ap.add_argument("--resolution", dest="runres", default=None, metavar="WIDTHxHEIGHT",
|
|
help=help_parser.help_text("resolution"))
|
|
ap.add_argument("--usefbx", dest="xdriver", action="store_const", const="fbdev",
|
|
help=help_parser.help_text("usefbx"))
|
|
ap.add_argument("--vnc", action="store_true", default=False,
|
|
help=help_parser.help_text("vnc"))
|
|
ap.add_argument("--vncconnect", metavar="HOST:PORT", help=help_parser.help_text("vncconnect"))
|
|
ap.add_argument("--vncpassword", default="", metavar="PASSWORD",
|
|
help=help_parser.help_text("vncpassword"))
|
|
ap.add_argument("--xdriver", dest="xdriver", action="store", type=str,
|
|
default=None, metavar="DRIVER", help=help_parser.help_text("xdriver"))
|
|
|
|
# Language
|
|
ap.add_argument("--keymap", metavar="KEYMAP", help=help_parser.help_text("keymap"))
|
|
ap.add_argument("--lang", metavar="LANG", help=help_parser.help_text("lang"))
|
|
|
|
# Obvious
|
|
ap.add_argument("--loglevel", metavar="LEVEL", help=help_parser.help_text("loglevel"))
|
|
ap.add_argument("--syslog", metavar="HOST[:PORT]", help=help_parser.help_text("syslog"))
|
|
|
|
from pykickstart.constants import SELINUX_DISABLED, SELINUX_ENFORCING
|
|
from pyanaconda.constants import SELINUX_DEFAULT
|
|
ap.add_argument("--noselinux", dest="selinux", action="store_const",
|
|
const=SELINUX_DISABLED, default=SELINUX_DEFAULT,
|
|
help=help_parser.help_text("noselinux"))
|
|
ap.add_argument("--selinux", action="store_const",
|
|
const=SELINUX_ENFORCING, help=help_parser.help_text("selinux"))
|
|
|
|
ap.add_argument("--nompath", dest="mpath", action="store_false", default=True,
|
|
help=help_parser.help_text("nompath"))
|
|
ap.add_argument("--mpath", action="store_true", help=help_parser.help_text("mpath"))
|
|
|
|
ap.add_argument("--nodmraid", dest="dmraid", action="store_false", default=True,
|
|
help=help_parser.help_text("nodmraid"))
|
|
ap.add_argument("--dmraid", action="store_true", help=help_parser.help_text("dmraid"))
|
|
|
|
ap.add_argument("--noibft", dest="ibft", action="store_false", default=True,
|
|
help=help_parser.help_text("noibft"))
|
|
ap.add_argument("--ibft", action="store_true", help=help_parser.help_text("ibft"))
|
|
|
|
# Geolocation
|
|
ap.add_argument("--geoloc", metavar="PROVIDER_ID", help=help_parser.help_text("geoloc"))
|
|
|
|
# Miscellaneous
|
|
ap.add_argument("--nomount", dest="rescue_nomount", action="store_true", default=False,
|
|
help=help_parser.help_text("nomount"))
|
|
ap.add_argument("--updates", dest="updateSrc", action="store", type=str,
|
|
metavar="UPDATES_URL", help=help_parser.help_text("updates"))
|
|
ap.add_argument("--image", action="append", dest="images", default=[],
|
|
metavar="IMAGE_SPEC", help=help_parser.help_text("image"))
|
|
ap.add_argument("--dirinstall", nargs="?", const=True, default=False,
|
|
help=help_parser.help_text("dirinstall"))
|
|
ap.add_argument("--memcheck", action="store_true", default=True,
|
|
help=help_parser.help_text("memcheck"))
|
|
ap.add_argument("--nomemcheck", action="store_false", dest="memcheck",
|
|
help=help_parser.help_text("nomemcheck"))
|
|
ap.add_argument("--leavebootorder", action="store_true", default=False,
|
|
help=help_parser.help_text("leavebootorder"))
|
|
ap.add_argument("--noeject", action="store_false", dest="eject", default=True,
|
|
help=help_parser.help_text("noeject"))
|
|
ap.add_argument("--extlinux", action="store_true", default=False,
|
|
help=help_parser.help_text("extlinux"))
|
|
ap.add_argument("--nombr", action="store_true", default=False,
|
|
help=help_parser.help_text("nombr"))
|
|
ap.add_argument("--dnf", action="store_true", default=False,
|
|
help=help_parser.help_text("dnf"))
|
|
ap.add_argument("--mpathfriendlynames", action="store_true", default=True,
|
|
help=help_parser.help_text("mpathfriendlynames"))
|
|
|
|
# some defaults change based on cmdline flags
|
|
if boot_cmdline is not None:
|
|
if "console" in boot_cmdline:
|
|
ap.set_defaults(display_mode="t")
|
|
|
|
namespace = ap.parse_args(argv, boot_cmdline=boot_cmdline)
|
|
return (namespace, ap.deprecated_bootargs)
|
|
|
|
def setupPythonPath():
|
|
# First add our updates path
|
|
sys.path.insert(0, '/tmp/updates/')
|
|
|
|
from pyanaconda.constants import ADDON_PATHS
|
|
# append ADDON_PATHS dirs at the end
|
|
sys.path.extend(ADDON_PATHS)
|
|
|
|
def setupEnvironment():
|
|
# Silly GNOME stuff
|
|
if 'HOME' in os.environ and not "XAUTHORITY" in os.environ:
|
|
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 "LD_PRELOAD" in os.environ:
|
|
del os.environ["LD_PRELOAD"]
|
|
|
|
def setupLoggingFromOpts(options):
|
|
if (options.debug or options.updateSrc) and not options.loglevel:
|
|
# debugging means debug logging if an explicit level hasn't been st
|
|
options.loglevel = "debug"
|
|
|
|
if options.loglevel and options.loglevel in anaconda_log.logLevelMap:
|
|
log.info("Switching logging level to %s", options.loglevel)
|
|
level = anaconda_log.logLevelMap[options.loglevel]
|
|
anaconda_log.logger.tty_loglevel = level
|
|
anaconda_log.setHandlersLevel(log, level)
|
|
storage_log = logging.getLogger("storage")
|
|
anaconda_log.setHandlersLevel(storage_log, level)
|
|
packaging_log = logging.getLogger("packaging")
|
|
anaconda_log.setHandlersLevel(packaging_log, level)
|
|
|
|
if options.syslog:
|
|
anaconda_log.logger.updateRemote(options.syslog)
|
|
|
|
def gtk_warning(title, reason):
|
|
from gi.repository import Gtk
|
|
dialog = Gtk.MessageDialog(type=Gtk.MessageType.ERROR,
|
|
buttons=Gtk.ButtonsType.CLOSE,
|
|
message_format=reason)
|
|
dialog.set_title(title)
|
|
dialog.run()
|
|
dialog.destroy()
|
|
|
|
# pylint: disable=redefined-outer-name
|
|
def check_memory(anaconda, options, display_mode=None):
|
|
from pyanaconda import isys
|
|
from pyanaconda.iutil import persistent_root_image
|
|
|
|
reason_strict = _("%(product_name)s requires %(needed_ram)s MB of memory to "
|
|
"install, but you only have %(total_ram)s MB on this machine.\n")
|
|
reason_graphical = _("The %(product_name)s graphical installer requires %(needed_ram)s "
|
|
"MB of memory, but you only have %(total_ram)s MB\n.")
|
|
|
|
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 options.rescue:
|
|
return
|
|
|
|
if not display_mode:
|
|
display_mode = anaconda.displayMode
|
|
|
|
reason = reason_strict
|
|
total_ram = int(isys.total_memory())
|
|
needed_ram = int(isys.MIN_RAM)
|
|
graphical_ram = int(isys.MIN_GUI_RAM)
|
|
|
|
# count the squashfs.img in if it is kept in RAM
|
|
if not persistent_root_image():
|
|
needed_ram += isys.SQUASHFS_EXTRA_RAM
|
|
graphical_ram += isys.SQUASHFS_EXTRA_RAM
|
|
|
|
log.info("check_memory(): total:%s, needed:%s, graphical:%s",
|
|
total_ram, needed_ram, graphical_ram)
|
|
|
|
if not options.memcheck:
|
|
log.warning("CHECK_MEMORY DISABLED")
|
|
return
|
|
|
|
reason_args = {"product_name": product.productName,
|
|
"needed_ram": needed_ram,
|
|
"total_ram": total_ram}
|
|
if needed_ram > total_ram:
|
|
from snack import SnackScreen, ButtonChoiceWindow
|
|
if options.liveinst:
|
|
# pylint: disable=logging-not-lazy
|
|
stdoutLog.warning(reason % reason_args)
|
|
gtk_warning(livecd_title, reason % reason_args)
|
|
else:
|
|
reason += reboot_extra
|
|
screen = SnackScreen()
|
|
ButtonChoiceWindow(screen, _('Fatal Error'),
|
|
reason % reason_args,
|
|
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_args["needed_ram"] = graphical_ram
|
|
reason = reason_graphical
|
|
|
|
if needed_ram > total_ram:
|
|
if options.liveinst:
|
|
reason += livecd_extra
|
|
# pylint: disable=logging-not-lazy
|
|
stdoutLog.warning(reason % reason_args)
|
|
title = livecd_title
|
|
gtk_warning(title, reason % reason_args)
|
|
sys.exit(1)
|
|
else:
|
|
reason += nolivecd_extra
|
|
# pylint: disable=logging-not-lazy
|
|
stdoutLog.warning(reason % reason_args)
|
|
anaconda.displayMode = 't'
|
|
time.sleep(2)
|
|
|
|
def startDebugger(signum, frame):
|
|
# pylint: disable=import-error
|
|
import epdb
|
|
epdb.serve(skip=1)
|
|
|
|
# pylint: disable=redefined-outer-name
|
|
def setupDisplay(anaconda, options, addons=None):
|
|
from pyanaconda.ui.tui.simpleline import App
|
|
from pyanaconda.ui.tui.spokes.askvnc import AskVNCSpoke
|
|
from pykickstart.constants import DISPLAY_MODE_TEXT
|
|
from pyanaconda.nm import nm_is_connected, nm_is_connecting
|
|
from blivet import arch
|
|
|
|
graphical_failed = 0
|
|
vncS = vnc.VncServer() # The vnc Server object.
|
|
vncS.anaconda = anaconda
|
|
|
|
anaconda.displayMode = options.display_mode
|
|
anaconda.isHeadless = arch.isS390()
|
|
|
|
if options.vnc:
|
|
flags.usevnc = True
|
|
anaconda.displayMode = 'g'
|
|
vncS.password = options.vncpassword
|
|
|
|
# Only consider vncconnect when vnc is a param
|
|
if options.vncconnect:
|
|
cargs = string.split(options.vncconnect, ":")
|
|
vncS.vncconnecthost = cargs[0]
|
|
if len(cargs) > 1 and len(cargs[1]) > 0:
|
|
if len(cargs[1]) > 0:
|
|
vncS.vncconnectport = cargs[1]
|
|
|
|
if options.xdriver:
|
|
anaconda.xdriver = options.xdriver
|
|
anaconda.writeXdriver(root="/")
|
|
|
|
if flags.rescue_mode:
|
|
return
|
|
|
|
if anaconda.ksdata.vnc.enabled:
|
|
flags.usevnc = True
|
|
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
|
|
|
|
if anaconda.displayMode == "g":
|
|
import pkgutil
|
|
import pyanaconda.ui
|
|
|
|
mods = (tup[1] for tup in pkgutil.iter_modules(pyanaconda.ui.__path__, "pyanaconda.ui."))
|
|
if "pyanaconda.ui.gui" not in mods:
|
|
stdoutLog.warning("Graphical user interface not available, falling back to text mode")
|
|
anaconda.displayMode = "t"
|
|
flags.usevnc = False
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC over text question when not enough memory is available
|
|
if blivet.util.total_memory() < isys.MIN_GUI_RAM:
|
|
stdoutLog.warning("Not asking for VNC because current memory (%d) < MIN_GUI_RAM (%d)", blivet.util.total_memory(), 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:
|
|
stdoutLog.warning("Not asking for VNC because of an automated install")
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC question if we were explicitly asked for text in kickstart
|
|
if anaconda.ksdata.displaymode.displayMode == DISPLAY_MODE_TEXT:
|
|
stdoutLog.warning("Not asking for VNC because text mode was explicitly asked for in kickstart")
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC question if we don't have network
|
|
if not nm_is_connecting() and not nm_is_connected():
|
|
stdoutLog.warning("Not asking for VNC because we don't have a network")
|
|
flags.vncquestion = False
|
|
|
|
# disable VNC question if we don't have Xvnc
|
|
if not os.access('/usr/bin/Xvnc', os.X_OK):
|
|
stdoutLog.warning("Not asking for VNC because we don't have Xvnc")
|
|
flags.vncquestion = False
|
|
|
|
if 'DISPLAY' in os.environ:
|
|
flags.preexisting_x11 = True
|
|
|
|
# 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 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 offer custom partitioning for "
|
|
"full control over the disk layout. 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 = True
|
|
vncS.password = anaconda.ksdata.vnc.password
|
|
|
|
log.info("Display mode = %s", anaconda.displayMode)
|
|
check_memory(anaconda, options)
|
|
|
|
# check_memory may have changed the display mode
|
|
want_x = want_x and (anaconda.displayMode == "g")
|
|
if want_x:
|
|
try:
|
|
startX11()
|
|
doStartupX11Actions()
|
|
except (OSError, RuntimeError) as e:
|
|
log.warning("X startup failed: %s", e)
|
|
stdoutLog.warning("X startup failed, falling back to text mode")
|
|
anaconda.displayMode = 't'
|
|
graphical_failed = 1
|
|
time.sleep(2)
|
|
|
|
if not graphical_failed:
|
|
doExtraX11Actions(options.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 = True
|
|
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(addons)
|
|
|
|
anaconda.instClass.configure(anaconda)
|
|
|
|
def prompt_for_ssh():
|
|
# Do some work here to get the ip addr / hostname to pass
|
|
# to the user.
|
|
import socket
|
|
|
|
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 socket.herror as e:
|
|
stdoutLog.debug("Exception caught trying to get host name of %s: %s", ipstr, e)
|
|
name = network.getHostname()
|
|
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."))
|
|
|
|
def cleanPStore():
|
|
"""remove files stored in nonvolatile ram created by the pstore subsystem"""
|
|
|
|
# files in pstore are linux (not distribution) specific, but we want to
|
|
# make sure the entirity of them are removed so as to ensure that there
|
|
# is sufficient free space on the flash part. On some machines this will
|
|
# take effect immediately, which is the best case. Unfortunately on some,
|
|
# an intervening reboot is needed."""
|
|
from pyanaconda.iutil import dir_tree_map
|
|
dir_tree_map("/sys/fs/pstore", os.unlink, files=True, dirs=False)
|
|
|
|
if __name__ == "__main__":
|
|
# check if the CLI help is requested and return it at once,
|
|
# without importing random stuff and spamming stdout
|
|
if ("--help" in sys.argv) or ("-h" in sys.argv) or ("--version" in sys.argv):
|
|
# we skip the full logging initialisation, but we need to do at least
|
|
# this much (redirect any log messages to stdout) to get rid of the
|
|
# harmless but annoying "no handlers found" message on stdout
|
|
import logging
|
|
log = logging.getLogger("anaconda")
|
|
log.addHandler(logging.StreamHandler(stream=sys.stdout))
|
|
parseArguments()
|
|
|
|
print("Starting installer, one moment...")
|
|
|
|
# Allow a file to be loaded as early as possible
|
|
try:
|
|
# pylint: disable=import-error,unused-import
|
|
import updates_disk_hook
|
|
except ImportError:
|
|
pass
|
|
|
|
# this handles setting up updates for pypackages to minimize the set needed
|
|
setupPythonUpdates()
|
|
setupPythonPath()
|
|
|
|
# init threading before Gtk can do anything and before we start using threads
|
|
# initThreading initializes the threadMgr instance, import it afterwards
|
|
from pyanaconda.threads import initThreading, AnacondaThread
|
|
initThreading()
|
|
from pyanaconda.threads import threadMgr
|
|
|
|
import gettext
|
|
_ = lambda x: gettext.ldgettext("anaconda", x)
|
|
|
|
from pyanaconda import constants
|
|
from pyanaconda.addons import collect_addon_paths
|
|
from pyanaconda import geoloc
|
|
|
|
# do this early so we can set flags before initializing logging
|
|
from pyanaconda.flags import flags, can_touch_runtime_system
|
|
(opts, depr) = parseArguments(boot_cmdline=flags.cmdline)
|
|
|
|
if opts.images:
|
|
flags.imageInstall = True
|
|
elif opts.dirinstall:
|
|
flags.dirInstall = True
|
|
|
|
# Set up logging as early as possible.
|
|
import logging
|
|
from pyanaconda import anaconda_log
|
|
anaconda_log.init()
|
|
anaconda_log.logger.setupVirtio()
|
|
|
|
from pyanaconda import network
|
|
network.setup_ifcfg_log()
|
|
|
|
log = logging.getLogger("anaconda")
|
|
stdoutLog = logging.getLogger("anaconda.stdout")
|
|
|
|
if os.geteuid() != 0:
|
|
stdoutLog.error("anaconda must be run as root.")
|
|
sys.exit(1)
|
|
|
|
# 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 not flags.ksprompt and not flags.imageInstall:
|
|
prompt_for_ssh()
|
|
sys.exit(0)
|
|
|
|
log.info("%s %s", sys.argv[0], getAnacondaVersionString())
|
|
if os.path.exists("/tmp/updates"):
|
|
log.info("Using updates in /tmp/updates/ from %s", opts.updateSrc)
|
|
|
|
# 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 import isys
|
|
|
|
import string
|
|
|
|
from pyanaconda import iutil
|
|
|
|
if opts.images and opts.dirinstall:
|
|
stdoutLog.error("--images and --dirinstall cannot be used at the same time")
|
|
sys.exit(1)
|
|
elif opts.dirinstall:
|
|
if opts.dirinstall is True:
|
|
root_path = os.environ.get("ANACONDA_ROOT_PATH", "/mnt/sysimage")
|
|
else:
|
|
root_path = opts.dirinstall
|
|
iutil.setTargetPhysicalRoot(root_path)
|
|
iutil.setSysroot(root_path)
|
|
|
|
from pyanaconda import vnc
|
|
from pyanaconda import kickstart
|
|
from pyanaconda import ntp
|
|
from pyanaconda import keyboard
|
|
from pyanaconda.iutil import ProxyString, ProxyStringError
|
|
|
|
verdesc = "%s for %s %s" % (getAnacondaVersionString(),
|
|
product.productName, product.productVersion)
|
|
|
|
logs_note = " * installation log files are stored in /tmp during the installation"
|
|
shell_and_tmux_note = " * shell is available on TTY2"
|
|
shell_only_note = " * shell is available on TTY2 and in second TMUX pane (ctrl+b, then press 2)"
|
|
tmux_only_note = " * shell is available in second TMUX pane (ctrl+b, then press 2)"
|
|
text_mode_note = " * if the graphical installation interface fails to start, try again with the\n"\
|
|
" inst.text bootoption to start text installation"
|
|
separate_attachements_note = " * when reporting a bug add logs from /tmp as separate text/plain attachments"
|
|
|
|
if product.isFinal:
|
|
print("anaconda %s started." % verdesc)
|
|
else:
|
|
print("anaconda %s (pre-release) started." % verdesc)
|
|
|
|
# we are past the --version and --help shortcut so we can import Blivet
|
|
# now without slowing down anything critical
|
|
|
|
# pylint: disable=import-error
|
|
from blivet import arch
|
|
|
|
if not opts.images and not opts.dirinstall:
|
|
print(logs_note)
|
|
# no fancy stuff like TTYs on a s390...
|
|
if not arch.isS390():
|
|
if "TMUX" in os.environ and os.environ.get("TERM") == "screen":
|
|
print(shell_and_tmux_note)
|
|
else:
|
|
print(shell_only_note) # TMUX is not running
|
|
# ...but there is apparently TMUX during the manual installation on s390!
|
|
elif not opts.ksfile:
|
|
print(tmux_only_note) # but not during kickstart installation
|
|
# no need to tell users how to switch to text mode
|
|
# if already in text mode
|
|
if opts.display_mode == 'g':
|
|
print(text_mode_note)
|
|
print(separate_attachements_note)
|
|
|
|
from pyanaconda.anaconda import Anaconda
|
|
anaconda = Anaconda()
|
|
iutil.setup_translations(gettext)
|
|
|
|
# reset python's default SIGINT handler
|
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
signal.signal(signal.SIGSEGV, isys.handleSegv)
|
|
signal.signal(signal.SIGTERM, lambda num, frame: sys.exit(1))
|
|
|
|
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
|
|
|
|
# assign the other anaconda variables from options
|
|
anaconda.proxy = opts.proxy
|
|
anaconda.updateSrc = opts.updateSrc
|
|
anaconda.methodstr = opts.method
|
|
anaconda.stage2 = opts.stage2
|
|
flags.rescue_mode = opts.rescue
|
|
|
|
if opts.liveinst:
|
|
from pyanaconda.screensaver import inhibit_screensaver
|
|
from pyanaconda import safe_dbus
|
|
|
|
flags.livecdInstall = True
|
|
|
|
try:
|
|
anaconda.dbus_session_connection = safe_dbus.get_new_session_connection()
|
|
except safe_dbus.DBusCallError as e:
|
|
log.info("Unable to connect to DBus session bus: %s", e)
|
|
else:
|
|
anaconda.dbus_inhibit_id = inhibit_screensaver(anaconda.dbus_session_connection)
|
|
elif "LIVECMD" in os.environ:
|
|
log.warning("Running via liveinst, but not setting flags.livecdInstall - this is for testing only")
|
|
|
|
# set flags
|
|
flags.noverifyssl = opts.noverifyssl
|
|
flags.armPlatform = opts.armPlatform
|
|
flags.extlinux = opts.extlinux
|
|
flags.nombr = opts.nombr
|
|
flags.dnf = opts.dnf
|
|
flags.mpathFriendlyNames = opts.mpathfriendlynames
|
|
flags.debug = opts.debug
|
|
flags.askmethod = opts.askmethod
|
|
flags.dmraid = opts.dmraid
|
|
flags.mpath = opts.mpath
|
|
flags.ibft = opts.ibft
|
|
flags.selinux = opts.selinux
|
|
flags.eject = opts.eject
|
|
|
|
# Switch to tty1 on exception in case something goes wrong during X start.
|
|
# This way if, for example, metacity doesn't start, we switch back to a
|
|
# text console with a traceback instead of being left looking at a blank
|
|
# screen. python-meh will replace this excepthook with its own handler
|
|
# once it gets going.
|
|
if not flags.imageInstall and not flags.livecdInstall \
|
|
and not flags.dirInstall:
|
|
def _earlyExceptionHandler(ty, value, traceback):
|
|
iutil.vtActivate(1)
|
|
return sys.__excepthook__(ty, value, traceback)
|
|
|
|
sys.excepthook = _earlyExceptionHandler
|
|
|
|
if can_touch_runtime_system("start audit daemon"):
|
|
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 OSError:
|
|
pass
|
|
|
|
log.info("anaconda called with cmdline = %s", sys.argv)
|
|
log.info("Default encoding = %s ", sys.getdefaultencoding())
|
|
|
|
os.system("udevadm control --env=ANACONDA=1")
|
|
|
|
# Collect all addon paths
|
|
addon_paths = collect_addon_paths(constants.ADDON_PATHS)
|
|
|
|
# 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 and not opts.liveinst:
|
|
flags.automatedInstall = True
|
|
flags.eject = False
|
|
ksFiles = [opts.ksfile]
|
|
else:
|
|
ksFiles = ["/tmp/updates/interactive-defaults.ks",
|
|
"/usr/share/anaconda/interactive-defaults.ks"]
|
|
|
|
for ks in ksFiles:
|
|
if not os.path.exists(ks):
|
|
continue
|
|
|
|
kickstart.preScriptPass(ks)
|
|
log.info("Parsing kickstart: " + ks)
|
|
ksdata = kickstart.parseKickstart(ks)
|
|
|
|
# Only load the first defaults file we find.
|
|
break
|
|
|
|
if not ksdata:
|
|
ksdata = kickstart.AnacondaKSHandler(addon_paths["ks"])
|
|
|
|
# Pick up any changes from interactive-defaults.ks that would
|
|
# otherwise be covered by the dracut KS parser.
|
|
if ksdata.bootloader.extlinux:
|
|
flags.extlinux = True
|
|
if ksdata.rescue.rescue:
|
|
flags.rescue_mode = 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:
|
|
if can_touch_runtime_system("activate keyboard"):
|
|
keyboard.activate_keyboard(ksdata.keyboard)
|
|
else:
|
|
# at least make sure we have all the values
|
|
keyboard.populate_missing_items(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
|
|
if anaconda.proxy:
|
|
ksdata.method.proxy = anaconda.proxy
|
|
|
|
# Setup proxy environmental variables so that pre/post scripts use it
|
|
# as well as libreport
|
|
try:
|
|
proxy = ProxyString(anaconda.proxy)
|
|
except ProxyStringError as e:
|
|
log.info("Failed to parse proxy \"%s\": %s", anaconda.proxy, e)
|
|
else:
|
|
# Set environmental variables to be used by pre/post scripts
|
|
os.environ["PROXY"] = proxy.noauth_url
|
|
os.environ["PROXY_USER"] = proxy.username or ""
|
|
os.environ["PROXY_PASSWORD"] = proxy.password or ""
|
|
|
|
# Variables used by curl, libreport, etc.
|
|
os.environ["http_proxy"] = proxy.url
|
|
os.environ["ftp_proxy"] = proxy.url
|
|
os.environ["HTTPS_PROXY"] = proxy.url
|
|
|
|
if flags.noverifyssl:
|
|
ksdata.method.noverifyssl = flags.noverifyssl
|
|
if opts.multiLib:
|
|
# sets yum's multilib_policy to "all" (as opposed to "best")
|
|
ksdata.packages.multiLib = opts.multiLib
|
|
|
|
# set ksdata.method based on anaconda.method if it isn't already set
|
|
if anaconda.methodstr and not ksdata.method.seen:
|
|
if anaconda.methodstr.startswith("cdrom"):
|
|
ksdata.method.method = "cdrom"
|
|
elif anaconda.methodstr.startswith("nfs"):
|
|
ksdata.method.method = "nfs"
|
|
(nfsOptions, server, path) = iutil.parseNfsUrl(anaconda.methodstr)
|
|
ksdata.method.server = server
|
|
ksdata.method.dir = path
|
|
ksdata.method.opts = nfsOptions
|
|
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") or \
|
|
anaconda.methodstr.startswith("file"):
|
|
ksdata.method.method = "url"
|
|
ksdata.method.url = anaconda.methodstr
|
|
# installation source specified by bootoption
|
|
# overrides source set from kickstart;
|
|
# the kickstart might have specified a mirror list,
|
|
# so we need to clear it here if plain url source is provided
|
|
# by a bootoption, because having both url & mirror list
|
|
# set at once is not supported and breaks Yum in
|
|
# unpredictable ways
|
|
ksdata.method.mirrorlist = None
|
|
elif anaconda.methodstr.startswith("livecd"):
|
|
ksdata.method.method = "harddrive"
|
|
device = anaconda.methodstr.split(":", 1)[1]
|
|
ksdata.method.partition = os.path.normpath(device)
|
|
else:
|
|
log.error("Unknown method: %s", anaconda.methodstr)
|
|
|
|
# Override the selinux state from kickstart if set on the command line
|
|
if flags.selinux != constants.SELINUX_DEFAULT:
|
|
ksdata.selinux.selinux = flags.selinux
|
|
|
|
from pyanaconda import localization
|
|
# Set the language before loading an interface, when it may be too late.
|
|
|
|
# check if the LANG environmental variable is set
|
|
env_lang = os.environ.get("LANG")
|
|
if env_lang is not None:
|
|
# parse it using langtable
|
|
env_langs = localization.get_language_locales(env_lang)
|
|
# if parsed LANG is the same as our default language - ignore it;
|
|
# otherwise use it as valid language candidate
|
|
if env_langs and env_langs[0] != constants.DEFAULT_LANG:
|
|
env_lang = env_langs[0] # the first language is the best match
|
|
else:
|
|
env_lang = None
|
|
|
|
requested_lang = opts.lang or ksdata.lang.lang or env_lang
|
|
|
|
if requested_lang:
|
|
locales = localization.get_language_locales(requested_lang)
|
|
if locales:
|
|
localization.setup_locale(locales[0], ksdata.lang)
|
|
ksdata.lang.seen = True
|
|
else:
|
|
log.error("Invalid locale '%s' given on command line or in kickstart", requested_lang)
|
|
else:
|
|
# no kickstart or bootoption - use default
|
|
localization.setup_locale(constants.DEFAULT_LANG, ksdata.lang)
|
|
|
|
import blivet
|
|
blivet.enable_installer_mode()
|
|
|
|
# now start the interface
|
|
setupDisplay(anaconda, opts, addon_paths)
|
|
|
|
# we now know in which mode we are going to run so store the information
|
|
from pykickstart.constants import DISPLAY_MODE_GRAPHICAL, DISPLAY_MODE_CMDLINE, DISPLAY_MODE_TEXT
|
|
mode_char_to_const = {'g': DISPLAY_MODE_GRAPHICAL, 't': DISPLAY_MODE_TEXT, 'c': DISPLAY_MODE_CMDLINE}
|
|
ksdata.displaymode.displayMode = mode_char_to_const[anaconda.displayMode]
|
|
|
|
# if we're in text mode, the resulting system should be too
|
|
# ...unless the kickstart specified otherwise
|
|
if anaconda.displayMode != 'g' and not anaconda.ksdata.xconfig.startX:
|
|
anaconda.ksdata.skipx.skipx = True
|
|
|
|
if flags.rescue_mode:
|
|
from pyanaconda import rescue
|
|
anaconda.intf = rescue.RescueInterface()
|
|
|
|
# Set flag to prompt for missing ks data
|
|
if anaconda.displayMode == 'c':
|
|
flags.ksprompt = False
|
|
|
|
from pyanaconda.anaconda_argparse import name_path_pairs
|
|
|
|
image_count = 0
|
|
try:
|
|
for (name, path) in name_path_pairs(opts.images):
|
|
log.info("naming disk image '%s' '%s'", path, name)
|
|
anaconda.storage.config.diskImages[name] = path
|
|
image_count += 1
|
|
flags.imageInstall = True
|
|
except ValueError as e:
|
|
stdoutLog.error("error specifying image file: %s", e)
|
|
sys.exit(1)
|
|
|
|
if image_count:
|
|
anaconda.storage.setupDiskImages()
|
|
|
|
# only install interactive exception handler in interactive modes
|
|
if ksdata.displaymode.displayMode != DISPLAY_MODE_CMDLINE or flags.debug:
|
|
from pyanaconda import exception
|
|
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())
|
|
atexit.register(exitHandler, ksdata.reboot, anaconda.storage)
|
|
|
|
from blivet import storageInitialize
|
|
from pyanaconda.packaging import payloadMgr
|
|
from pyanaconda.network import networkInitialize, wait_for_connecting_NM_thread
|
|
from pyanaconda.timezone import time_initialize
|
|
|
|
if flags.rescue_mode:
|
|
rescue.doRescue(anaconda.intf, anaconda.rescue_mount, ksdata)
|
|
else:
|
|
cleanPStore()
|
|
|
|
networkInitialize(ksdata)
|
|
if not flags.dirInstall:
|
|
threadMgr.add(AnacondaThread(name=constants.THREAD_STORAGE, target=storageInitialize,
|
|
args=(anaconda.storage, ksdata, anaconda.protected)))
|
|
threadMgr.add(AnacondaThread(name=constants.THREAD_TIME_INIT, target=time_initialize,
|
|
args=(ksdata.timezone, anaconda.storage, anaconda.bootloader)))
|
|
|
|
threadMgr.add(AnacondaThread(name=constants.THREAD_WAIT_FOR_CONNECTING_NM, target=wait_for_connecting_NM_thread, args=(ksdata,)))
|
|
|
|
# Fallback to default for interactive or for a kickstart with no installation method.
|
|
fallback = not (flags.automatedInstall and ksdata.method.method)
|
|
payloadMgr.restartThread(anaconda.storage, ksdata, anaconda.payload, anaconda.instClass,
|
|
fallback=fallback)
|
|
|
|
# check if geolocation should be enabled for this type of installation
|
|
use_geolocation = True
|
|
if flags.imageInstall or flags.dirInstall or flags.automatedInstall:
|
|
use_geolocation = False
|
|
# and also check if it was not disabled by boot option
|
|
else:
|
|
# flags.cmdline.getbool is used as it handles values such as
|
|
# 0, no, off and also nogeoloc as False
|
|
# and other values or geoloc not being present as True
|
|
use_geolocation = flags.cmdline.getbool('geoloc', True)
|
|
|
|
if use_geolocation:
|
|
provider_id = constants.GEOLOC_DEFAULT_PROVIDER
|
|
# check if a provider was specified by an option
|
|
if opts.geoloc is not None:
|
|
parsed_id = geoloc.get_provider_id_from_option(opts.geoloc)
|
|
if parsed_id is None:
|
|
log.error('geoloc: wrong provider id specified: %s', opts.geoloc)
|
|
else:
|
|
provider_id = parsed_id
|
|
# instantiate the geolocation module and start location data refresh
|
|
geoloc.init_geolocation(provider_id=provider_id)
|
|
geoloc.refresh()
|
|
|
|
# setup ntp servers and start NTP daemon if not requested otherwise
|
|
if can_touch_runtime_system("start chronyd"):
|
|
if anaconda.ksdata.timezone.ntpservers:
|
|
ntp.save_servers_to_config(anaconda.ksdata.timezone.ntpservers)
|
|
|
|
if not anaconda.ksdata.timezone.nontp:
|
|
iutil.start_service("chronyd")
|
|
|
|
# try to load firmware language
|
|
localization.load_firmware_language(ksdata.lang)
|
|
|
|
# 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
|