[PATCH 2/2] Make Initial Setup startup more robust

Martin Kolman mkolman at redhat.com
Fri Nov 13 17:21:12 UTC 2015


Use just a single systemd service, named initial-setup.service, in place
of the two text and graphical services. This should improve
robustness by placing less emphasis on systemd correctly resolving
unit dependencies.

The single unit file will run a shell script that check if the GUI executable
exists and runs it if it does. If the is no GUI executable the TUI executable will be run.

All scripts and executables have also been moved to /usr/libexec
to make it readily apparent they are not meant to be executed manually
during normal operation.

On the other hand it is now easier to debug Initial Setup startup issues
manually when one *does* run the executables by hand from the
/usr/libexec/initial-setup/ folder as log messages are now printed to stdout
and the usual --help and --version flags are supported.

Furthermore the core Initial Setup file has been refactored to be
easier to maintain, more docstrings have been added and the current
Initial Setup version is logged at start of the run.

Signed-off-by: Martin Kolman <mkolman at redhat.com>
---
 Makefile                                |   3 +-
 firstboot-windowmanager                 |  47 -----
 initial-setup                           |   9 -
 initial-setup.spec                      |  24 +--
 initial_setup/__init__.py               | 310 ++++++++++++++++++++++++++++++++
 initial_setup/__main__.py               | 216 ----------------------
 initial_setup/initial_setup_log.py      |  10 +-
 scripts/firstboot-windowmanager         |  47 +++++
 scripts/initial-setup-graphical         |  14 ++
 scripts/initial-setup-text              |  14 ++
 scripts/run-initial-setup               |  26 +++
 scripts/s390/initial-setup.csh          |   4 +-
 scripts/s390/initial-setup.sh           |   4 +-
 setup.py                                |   6 +-
 systemd/initial-setup-graphical.service |  17 --
 systemd/initial-setup-text.service      |  26 ---
 systemd/initial-setup.service           |  26 +++
 17 files changed, 464 insertions(+), 339 deletions(-)
 delete mode 100755 firstboot-windowmanager
 delete mode 100755 initial-setup
 delete mode 100644 initial_setup/__main__.py
 create mode 100755 scripts/firstboot-windowmanager
 create mode 100755 scripts/initial-setup-graphical
 create mode 100755 scripts/initial-setup-text
 create mode 100755 scripts/run-initial-setup
 delete mode 100644 systemd/initial-setup-graphical.service
 delete mode 100644 systemd/initial-setup-text.service
 create mode 100644 systemd/initial-setup.service

diff --git a/Makefile b/Makefile
index 0683d28..a49bc82 100644
--- a/Makefile
+++ b/Makefile
@@ -102,6 +102,7 @@ bumpver: potfile
 	(head -n $$cl initial-setup.spec ; echo "$$DATELINE" ; make --quiet rpmlog 2>/dev/null ; echo ""; cat speclog) > initial-setup.spec.new ; \
 	mv initial-setup.spec.new initial-setup.spec ; rm -f speclog ; \
 	sed -i "s/Version: $(VERSION)/Version: $$NEWVERSION/" initial-setup.spec ; \
-	sed -i "s/version = \"$(VERSION)\"/version = \"$$NEWVERSION\"/" setup.py
+	sed -i "s/version = \"$(VERSION)\"/version = \"$$NEWVERSION\"/" setup.py ; \
+	sed -i "s/__version__ = \"$(VERSION)\"/__version__ = \"$$NEWVERSION\"/" initial_setup/__init__.py ; \
 
 .PHONY: clean install tag archive local
diff --git a/firstboot-windowmanager b/firstboot-windowmanager
deleted file mode 100755
index bfb32cb..0000000
--- a/firstboot-windowmanager
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/bin/sh
-
-# This is the list of supported window manager binaries
-WMS=("run_metacity" "kwin_x11" "kwin" "xfwm4" "openbox" "marco")
-
-run_metacity()
-{
-    # Apply the anaconda overrides before running metacity
-    if [ -z "$XDG_DATA_DIRS" ] ; then
-        new_data_dirs="/usr/share/anaconda/window-manager:/usr/share"
-    else
-        new_data_dirs="/usr/share/anaconda/window-manager:${XDG_DATA_DIRS}"
-    fi
-    XDG_DATA_DIRS="$new_data_dirs" metacity
-}
-
-# Get the application binary to start and remove it from
-# the argument list
-BINARY=$1
-shift
-
-for WM in ${WMS[@]}; do
-    FILE=$(which $WM 2>/dev/null)
-    FOUND=$?
-
-    if [ $FOUND -eq 0 -a -x "$FILE" ]; then
-        # start window manager
-        echo "Running $FOUND window manager ($FILE)" | logger
-        "$FILE" "$@" &
-        pid=$!
-
-        # start the application
-        echo "Running $BINARY" | logger
-        $BINARY
-        res=$?
-
-        # stop window manager
-        ps -p $pid >/dev/null && kill $pid
-
-        # return result
-        exit $res
-    fi
-done
-
-# No known window manager found
-echo "No supported window manager found!" | logger
-exit 1
diff --git a/initial-setup b/initial-setup
deleted file mode 100755
index 34b1feb..0000000
--- a/initial-setup
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-echo "Running initial_setup" | logger
-if python3 -m initial_setup; then
-    echo "initial_setup finished successfully, disabling" | logger
-    /bin/systemctl disable initial-setup-graphical.service initial-setup-text.service &>/dev/null
-else
-    echo "initial_setup failed, keeping enabled" | logger
-fi
diff --git a/initial-setup.spec b/initial-setup.spec
index d78b462..813e713 100644
--- a/initial-setup.spec
+++ b/initial-setup.spec
@@ -101,13 +101,13 @@ if [ $1 -gt 1 ] ; then
 fi
 
 %post
-%systemd_post initial-setup-text.service
+%systemd_post initial-setup.service
 
 %preun
-%systemd_preun initial-setup-text.service
+%systemd_post initial-setup.service
 
 %postun
-%systemd_postun initial-setup-text.service
+%systemd_post initial-setup.service
 
 %pre gui
 # There is a possibility that an initial setup service might be running
@@ -136,22 +136,14 @@ if [ $1 -gt 1 ] ; then
     fi
 fi
 
-%post gui
-%systemd_post initial-setup-graphical.service
-
-%preun gui
-%systemd_preun initial-setup-graphical.service
-
-%postun gui
-%systemd_postun initial-setup-graphical.service
-
 %files -f %{name}.lang
 %doc COPYING README.rst
 %{python3_sitelib}/initial_setup*
 %exclude %{python3_sitelib}/initial_setup/gui
-%{_bindir}/initial-setup
-%{_bindir}/firstboot-windowmanager
-%{_unitdir}/initial-setup-text.service
+%{_libexecdir}/%{name}/run-initial-setup
+%{_libexecdir}/%{name}/firstboot-windowmanager
+%{_libexecdir}/%{name}/initial-setup-text
+%{_unitdir}/initial-setup.service
 
 %ifarch s390 s390x
 %{_sysconfdir}/profile.d/initial-setup.sh
@@ -159,8 +151,8 @@ fi
 %endif
 
 %files gui
+%{_libexecdir}/%{name}/initial-setup-graphical
 %{python3_sitelib}/initial_setup/gui/*
-%{_unitdir}/initial-setup-graphical.service
 
 %changelog
 * Wed Sep 30 2015 Martin Kolman <mkolman at redhat.com> - 0.3.37-1
diff --git a/initial_setup/__init__.py b/initial_setup/__init__.py
index e69de29..fab9e6e 100644
--- a/initial_setup/__init__.py
+++ b/initial_setup/__init__.py
@@ -0,0 +1,310 @@
+#!/bin/python
+
+__version__ = "0.3.37"
+
+import os
+import sys
+import signal
+import pykickstart
+import logging
+import argparse
+from pyanaconda.users import Users
+from initial_setup.post_installclass import PostInstallClass
+from initial_setup import initial_setup_log
+from pyanaconda import iutil
+from pykickstart.constants import FIRSTBOOT_RECONFIG
+from pyanaconda.localization import setup_locale_environment, setup_locale
+
+class InitialSetupError(Exception):
+    pass
+
+INPUT_KICKSTART_PATH = "/root/anaconda-ks.cfg"
+OUTPUT_KICKSTART_PATH = "/root/initial-setup-ks.cfg"
+RECONFIG_FILE = "/etc/reconfigSys"
+
+SUPPORTED_KICKSTART_COMMANDS = ["user",
+                                "group",
+                                "keyboard",
+                                "lang",
+                                "rootpw",
+                                "timezone",
+                                "logging",
+                                "selinux",
+                                "firewall"]
+
+# set root to "/", we are now in the installed system
+iutil.setSysroot("/")
+
+signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+external_reconfig = os.path.exists(RECONFIG_FILE)
+
+# setup logging
+log = logging.getLogger("initial-setup")
+
+class InitialSetup(object):
+    def __init__(self, gui_mode):
+        """Initialize the Initial Setup internals"""
+        log.debug("initializing Initial Setup")
+        self._gui_mode = False
+        self._data = None
+
+        self.gui_mode = gui_mode
+
+        # parse any command line arguments
+        args = self._parse_arguments()
+
+        # initialize logging
+        initial_setup_log.init(stdout_log=not args.no_stdout_log)
+
+        log.info("Initial Setup %s" % __version__)
+
+        # check if we are running as root
+        if os.geteuid() != 0:
+            log.critical("Initial Setup needs to be run as root")
+            raise InitialSetupError
+
+        if self.gui_mode:
+            log.debug("running in GUI mode")
+        else:
+            log.debug("running in TUI mode")
+
+        if external_reconfig:
+            log.debug("running in externally triggered reconfig mode")
+
+        if self.gui_mode:
+            # We need this so we can tell GI to look for overrides objects
+            # also in anaconda source directories
+            import gi.overrides
+            for p in os.environ.get("ANACONDA_WIDGETS_OVERRIDES", "").split(":"):
+                gi.overrides.__path__.insert(0, p)
+            log.debug("GI overrides imported")
+
+        from pyanaconda.addons import collect_addon_paths
+
+        addon_paths = ["/usr/share/initial-setup/modules", "/usr/share/anaconda/addons"]
+
+        # append ADDON_PATHS dirs at the end
+        sys.path.extend(addon_paths)
+
+        self._addon_module_paths = collect_addon_paths(addon_paths, self.gui_mode_id)
+        log.info("found %d addon modules:", len(self._addon_module_paths))
+        for addon_path in self._addon_module_paths:
+            log.debug(addon_path)
+
+        # Too bad anaconda does not have modularized logging
+        log.debug("initializing the Anaconda log")
+        from pyanaconda import anaconda_log
+        anaconda_log.init()
+
+        # init threading before Gtk can do anything and before we start using threads
+        # initThreading initializes the threadMgr instance, import it afterwards
+        log.debug("initializing threading")
+        from pyanaconda.threads import initThreading
+        initThreading()
+
+        # initialize network logging (needed by the Network spoke that may be shown)
+        log.debug("initializing network logging")
+        from pyanaconda.network import setup_ifcfg_log
+        setup_ifcfg_log()
+
+    @property
+    def gui_mode(self):
+        """GUI mode
+
+        :returns: True if running in graphical mode, False otherwise (text mode)
+        :rtype bool:
+        """
+        return self._gui_mode
+
+    @gui_mode.setter
+    def gui_mode(self, enabled):
+        self._gui_mode = enabled
+
+    @property
+    def gui_mode_id(self):
+        """String id of the current GUI mode
+
+        :returns: "gui" if gui_mode is True, "tui" otherwise
+        :rtype str:
+        """
+        if self.gui_mode:
+            return "gui"
+        else:
+            return "tui"
+
+    @property
+    def data(self):
+        """A kickstart data instance"""
+        return self._data
+
+    @data.setter
+    def data(self, ks_data):
+        self._data = ks_data
+
+    def _parse_arguments(self):
+        """Parse command line arguments"""
+        # create an argparse instance
+        parser = argparse.ArgumentParser(prog="Initial Setup",
+                                         description="Initial Setup is can run during the first start of a newly installed"
+                                         "system to configure it according to the needs of the user.")
+        parser.add_argument("--no-stdout-log", action="store_true", default=False, help="Don't log to stdout.")
+        parser.add_argument('--version', action='version', version=__version__)
+
+        # parse arguments and return the result
+        return parser.parse_args()
+
+    def _load_kickstart(self):
+        """Load the kickstart"""
+        from pyanaconda import kickstart
+
+        # Construct a commandMap with only the supported Anaconda's commands
+        commandMap = dict((k, kickstart.commandMap[k]) for k in SUPPORTED_KICKSTART_COMMANDS)
+
+        # Prepare new data object
+        self.data = kickstart.AnacondaKSHandler(self._addon_module_paths["ks"], commandUpdates=commandMap)
+
+        kickstart_path = INPUT_KICKSTART_PATH
+        if os.path.exists(OUTPUT_KICKSTART_PATH):
+            log.info("using kickstart from previous run for input")
+            kickstart_path = OUTPUT_KICKSTART_PATH
+
+        log.info("parsing input kickstart %s", kickstart_path)
+        try:
+            # Read the installed kickstart
+            parser = kickstart.AnacondaKSParser(self.data)
+            parser.readKickstart(kickstart_path)
+            log.info("kickstart parsing done")
+        except pykickstart.errors.KickstartError as kserr:
+            log.critical("kickstart parsing failed: %s", kserr)
+            log.critical("Initial Setup startup failed due to invalid kickstart file")
+            raise InitialSetupError
+
+        if external_reconfig:
+            # set the reconfig flag in kickstart so that
+            # relevant spokes show up
+            self.data.firstboot.firstboot = FIRSTBOOT_RECONFIG
+
+    def _setup_locale(self):
+        log.debug("setting up locale")
+        # Normalize the locale environment variables
+        if self.data.lang.seen:
+            locale_arg = self.data.lang.lang
+        else:
+            locale_arg = None
+        setup_locale_environment(locale_arg, prefer_environment=True)
+        setup_locale(os.environ['LANG'], text_mode=not self.gui_mode)
+
+    def _apply(self):
+        # Do not execute sections that were part of the original
+        # anaconda kickstart file (== have .seen flag set)
+
+        sections = [self.data.keyboard, self.data.lang, self.data.timezone]
+
+        # data.selinux
+        # data.firewall
+
+        log.info("executing kickstart")
+        for section in sections:
+            section_msg = "%s on line %d" % (repr(section), section.lineno)
+            if section.seen:
+                log.debug("skipping %s", section_msg)
+                continue
+            log.debug("executing %s", section_msg)
+            section.execute(None, self.data, None)
+
+        # Prepare the user database tools
+        u = Users()
+
+        sections = [self.data.group, self.data.user, self.data.rootpw]
+        for section in sections:
+            section_msg = "%s on line %d" % (repr(section), section.lineno)
+            if section.seen:
+                log.debug("skipping %s", section_msg)
+                continue
+            log.debug("executing %s", section_msg)
+            section.execute(None, self.data, None, u)
+
+        # Configure all addons
+        log.info("executing addons")
+        self.data.addons.execute(None, self.data, None, u)
+
+        if external_reconfig:
+            # prevent the reconfig flag from being written out,
+            # to prevent the reconfig mode from being enabled
+            # without the /etc/reconfigSys file being present
+            self.data.firstboot.firstboot = None
+
+        # Write the kickstart data to file
+        log.info("writing the Initial Setup kickstart file %s", OUTPUT_KICKSTART_PATH)
+        with open(OUTPUT_KICKSTART_PATH, "w") as f:
+            f.write(str(self.data))
+        log.info("finished writing the Initial Setup kickstart file")
+
+        # Remove the reconfig file, if any - otherwise the reconfig mode
+        # would start again next time the Initial Setup service is enabled
+        if external_reconfig and os.path.exists(RECONFIG_FILE):
+            log.debug("removing the reconfig file")
+            os.remove(RECONFIG_FILE)
+            log.debug("the reconfig file has been removed")
+
+    def run(self):
+        """Run Initial setup
+
+        :param bool gui_mode: if GUI should be used (TUI is the default)
+
+        :returns: True if the IS run was successfull, False if it failed
+        :rtype: bool
+        """
+
+        self._load_kickstart()
+        self._setup_locale()
+
+        if self.gui_mode:
+            try:
+                # Try to import IS gui specifics
+                log.debug("trying to import GUI")
+                import initial_setup.gui
+            except ImportError:
+                log.exception("GUI import failed, falling back to TUI")
+                self.gui_mode = False
+
+        if self.gui_mode:
+            # gui already imported (see above)
+
+            # Add addons to search paths
+            initial_setup.gui.InitialSetupGraphicalUserInterface.update_paths(self._addon_module_paths)
+
+            # Initialize the UI
+            log.debug("initializing GUI")
+            ui = initial_setup.gui.InitialSetupGraphicalUserInterface(None, None, PostInstallClass())
+        else:
+            # Import IS gui specifics
+            import initial_setup.tui
+
+            # Add addons to search paths
+            initial_setup.tui.InitialSetupTextUserInterface.update_paths(self._addon_module_paths)
+
+            # Initialize the UI
+            log.debug("initializing TUI")
+            ui = initial_setup.tui.InitialSetupTextUserInterface(None, None, None)
+
+        # Pass the data object to user inteface
+        log.debug("setting up the UI")
+        ui.setup(self.data)
+
+        # Start the application
+        log.info("starting the UI")
+        ret = ui.run()
+
+        # TUI returns False if the app was ended prematurely
+        # all other cases return True or None
+        if ret is False:
+            log.warning("ended prematurely in TUI")
+            return True
+
+        # apply changes
+        self._apply()
+
+        # and we are done
+        return True
diff --git a/initial_setup/__main__.py b/initial_setup/__main__.py
deleted file mode 100644
index 1fbff51..0000000
--- a/initial_setup/__main__.py
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/bin/python
-import os
-import sys
-import signal
-import pykickstart
-import logging
-from pyanaconda.users import Users
-from initial_setup.post_installclass import PostInstallClass
-from initial_setup import initial_setup_log
-from pyanaconda import iutil
-from pykickstart.constants import FIRSTBOOT_RECONFIG
-from pyanaconda.localization import setup_locale_environment, setup_locale
-
-INPUT_KICKSTART_PATH = "/root/anaconda-ks.cfg"
-OUTPUT_KICKSTART_PATH = "/root/initial-setup-ks.cfg"
-RECONFIG_FILE = "/etc/reconfigSys"
-
-# set root to "/", we are now in the installed system
-iutil.setSysroot("/")
-
-signal.signal(signal.SIGINT, signal.SIG_IGN)
-
-external_reconfig = os.path.exists(RECONFIG_FILE)
-
-initial_setup_log.init()
-log = logging.getLogger("initial-setup")
-
-if "DISPLAY" in os.environ and os.environ["DISPLAY"]:
-    mode="gui"
-else:
-    mode="tui"
-
-log.debug("display mode detected: %s", mode)
-
-if external_reconfig:
-    log.debug("running in externally triggered reconfig mode")
-
-if mode == "gui":
-    # We need this so we can tell GI to look for overrides objects
-    # also in anaconda source directories
-    import gi.overrides
-    for p in os.environ.get("ANACONDA_WIDGETS_OVERRIDES", "").split(":"):
-        gi.overrides.__path__.insert(0, p)
-    log.debug("GI overrides imported")
-
-from pyanaconda.addons import collect_addon_paths
-
-addon_paths = ["/usr/share/initial-setup/modules", "/usr/share/anaconda/addons"]
-
-# append ADDON_PATHS dirs at the end
-sys.path.extend(addon_paths)
-
-addon_module_paths = collect_addon_paths(addon_paths, mode)
-log.info("found %d addon modules:", len(addon_module_paths))
-for addon_path in addon_module_paths:
-    log.debug(addon_path)
-
-# Too bad anaconda does not have modularized logging
-log.debug("initializing the Anaconda log")
-from pyanaconda import anaconda_log
-anaconda_log.init()
-
-
-# init threading before Gtk can do anything and before we start using threads
-# initThreading initializes the threadMgr instance, import it afterwards
-log.debug("initializing threading")
-from pyanaconda.threads import initThreading
-initThreading()
-
-# initialize network logging (needed by the Network spoke that may be shown)
-log.debug("initializing network logging")
-from pyanaconda.network import setup_ifcfg_log
-setup_ifcfg_log()
-
-from pyanaconda import kickstart
-
-# Construct a commandMap with the supported Anaconda's commands only
-kickstart_commands = ["user",
-                      "group",
-                      "keyboard",
-                      "lang",
-                      "rootpw",
-                      "timezone",
-                      "logging",
-                      "selinux",
-                      "firewall",
-                      ]
-
-commandMap = dict((k, kickstart.commandMap[k]) for k in kickstart_commands)
-
-# Prepare new data object
-data = kickstart.AnacondaKSHandler(addon_module_paths["ks"], commandUpdates=commandMap)
-
-kickstart_path = INPUT_KICKSTART_PATH
-if os.path.exists(OUTPUT_KICKSTART_PATH):
-    log.info("using kickstart from previous run for input")
-    kickstart_path = OUTPUT_KICKSTART_PATH
-
-log.info("parsing input kickstart %s", kickstart_path)
-try:
-    # Read the installed kickstart
-    parser = kickstart.AnacondaKSParser(data)
-    parser.readKickstart(kickstart_path)
-    log.info("kickstart parsing done")
-except pykickstart.errors.KickstartError as kserr:
-    log.critical("kickstart parsing failed: %s", kserr)
-    log.critical("Initial Setup startup failed due to invalid kickstart file")
-    sys.exit(1)
-
-if external_reconfig:
-    # set the reconfig flag in kickstart so that
-    # relevant spokes show up
-    data.firstboot.firstboot = FIRSTBOOT_RECONFIG
-
-# Normalize the locale environment variables
-if data.lang.seen:
-    locale_arg = data.lang.lang
-else:
-    locale_arg = None
-setup_locale_environment(locale_arg, prefer_environment=True)
-setup_locale(os.environ['LANG'], text_mode=mode != "gui")
-
-if mode == "gui":
-    try:
-        # Try to import IS gui specifics
-        log.debug("trying to import GUI")
-        import initial_setup.gui
-    except ImportError:
-        log.exception("GUI import failed, falling back to TUI")
-        mode = "tui"
-
-if mode == "gui":
-    # gui already imported (see above)
-
-    # Add addons to search paths
-    initial_setup.gui.InitialSetupGraphicalUserInterface.update_paths(addon_module_paths)
-
-    # Initialize the UI
-    log.debug("initializing GUI")
-    ui = initial_setup.gui.InitialSetupGraphicalUserInterface(None, None, PostInstallClass())
-else:
-    # Import IS gui specifics
-    import initial_setup.tui
-
-    # Add addons to search paths
-    initial_setup.tui.InitialSetupTextUserInterface.update_paths(addon_module_paths)
-
-    # Initialize the UI
-    log.debug("initializing TUI")
-    ui = initial_setup.tui.InitialSetupTextUserInterface(None, None, None)
-
-# Pass the data object to user inteface
-log.debug("setting up the UI")
-ui.setup(data)
-
-# Start the application
-log.info("starting the UI")
-ret = ui.run()
-
-# TUI returns False if the app was ended prematurely
-# all other cases return True or None
-if ret == False:
-    log.warning("ended prematurely in TUI")
-    sys.exit(0)
-
-# Do not execute sections that were part of the original
-# anaconda kickstart file (== have .seen flag set)
-
-sections = [data.keyboard, data.lang, data.timezone]
-
-# data.selinux
-# data.firewall
-
-log.info("executing kickstart")
-for section in sections:
-    section_msg = "%s on line %d" % (repr(section), section.lineno)
-    if section.seen:
-        log.debug("skipping %s", section_msg)
-        continue
-    log.debug("executing %s", section_msg)
-    section.execute(None, data, None)
-
-# Prepare the user database tools
-u = Users()
-
-sections = [data.group, data.user, data.rootpw]
-for section in sections:
-    section_msg = "%s on line %d" % (repr(section), section.lineno)
-    if section.seen:
-        log.debug("skipping %s", section_msg)
-        continue
-    log.debug("executing %s", section_msg)
-    section.execute(None, data, None, u)
-
-# Configure all addons
-log.info("executing addons")
-data.addons.execute(None, data, None, u)
-
-if external_reconfig:
-    # prevent the reconfig flag from being written out,
-    # to prevent the reconfig mode from being enabled
-    # without the /etc/reconfigSys file being present
-    data.firstboot.firstboot = None
-
-# Write the kickstart data to file
-log.info("writing the Initial Setup kickstart file %s", OUTPUT_KICKSTART_PATH)
-with open(OUTPUT_KICKSTART_PATH, "w") as f:
-    f.write(str(data))
-log.info("finished writing the Initial Setup kickstart file")
-
-# Remove the reconfig file, if any - otherwise the reconfig mode
-# would start again next time the Initial Setup service is enabled
-if external_reconfig and os.path.exists(RECONFIG_FILE):
-    log.debug("removing the reconfig file")
-    os.remove(RECONFIG_FILE)
-    log.debug("the reconfig file has been removed")
diff --git a/initial_setup/initial_setup_log.py b/initial_setup/initial_setup_log.py
index fee1370..4623862 100644
--- a/initial_setup/initial_setup_log.py
+++ b/initial_setup/initial_setup_log.py
@@ -41,10 +41,18 @@ class InitialSetupSyslogHandler(SysLogHandler):
         SysLogHandler.emit(self, record)
         record.msg = original_msg
 
-def init():
+def init(stdout_log):
     """Initialize the Initial Setup logging system"""
     log = logging.getLogger("initial-setup")
     log.setLevel(logging.DEBUG)
     syslogHandler = InitialSetupSyslogHandler('/dev/log', SysLogHandler.LOG_LOCAL1, "initial-setup")
     syslogHandler.setLevel(logging.DEBUG)
     log.addHandler(syslogHandler)
+
+    if stdout_log:
+        # also log to stdout because someone is apparently running Initial Setup manually,
+        # probably for debugging purposes
+        stdoutHandler = logging.StreamHandler()
+        stdoutHandler.setLevel(logging.DEBUG)
+        stdoutHandler.setFormatter(logging.Formatter('%(levelname)s %(name)s: %(message)s'))
+        log.addHandler(stdoutHandler)
diff --git a/scripts/firstboot-windowmanager b/scripts/firstboot-windowmanager
new file mode 100755
index 0000000..5159c63
--- /dev/null
+++ b/scripts/firstboot-windowmanager
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# This is the list of supported window manager binaries
+WMS=("run_metacity" "kwin_x11" "kwin" "xfwm4" "openbox" "marco")
+
+run_metacity()
+{
+    # Apply the anaconda overrides before running metacity
+    if [ -z "$XDG_DATA_DIRS" ] ; then
+        new_data_dirs="/usr/share/anaconda/window-manager:/usr/share"
+    else
+        new_data_dirs="/usr/share/anaconda/window-manager:${XDG_DATA_DIRS}"
+    fi
+    XDG_DATA_DIRS="$new_data_dirs" metacity
+}
+
+# Get the application binary to start and remove it from
+# the argument list
+BINARY=$1
+shift
+
+for WM in ${WMS[@]}; do
+    FILE=$(which $WM 2>/dev/null)
+    FOUND=$?
+
+    if [ $FOUND -eq 0 -a -x "$FILE" ]; then
+        # start window manager
+        printf "MESSAGE=Running $FOUND window manager ($FILE)\nPRIORITY=6" | logger --journald
+        "$FILE" "$@" &
+        pid=$!
+
+        # start the application
+        printf "MESSAGE=Running ${BINARY}\nPRIORITY=6" | logger --journald
+        $BINARY
+        res=$?
+
+        # stop window manager
+        ps -p $pid >/dev/null && kill $pid
+
+        # return result
+        exit $res
+    fi
+done
+
+# No known window manager found
+printf "MESSAGE=No supported window manager found!\nPRIORITY=3" | logger --journald
+exit 1
diff --git a/scripts/initial-setup-graphical b/scripts/initial-setup-graphical
new file mode 100755
index 0000000..6e078ea
--- /dev/null
+++ b/scripts/initial-setup-graphical
@@ -0,0 +1,14 @@
+#!/usr/bin/python3
+
+from initial_setup import InitialSetup, InitialSetupError
+
+is_instance = InitialSetup(gui_mode=True)
+
+try:
+    is_instance.run()
+except InitialSetupError:
+    # Initial Setup with graphical interface apparently failed
+    exit(1)
+
+# Initial Setup with graphical interface completed successfully
+exit(0)
diff --git a/scripts/initial-setup-text b/scripts/initial-setup-text
new file mode 100755
index 0000000..30cbd56
--- /dev/null
+++ b/scripts/initial-setup-text
@@ -0,0 +1,14 @@
+#!/usr/bin/python3
+
+from initial_setup import InitialSetup, InitialSetupError
+
+is_instance = InitialSetup(gui_mode=False)
+
+try:
+    is_instance.run()
+except InitialSetupError:
+    # Initial Setup using with text interface apparently failed
+    exit(1)
+
+# Initial Setup using with text interface completed successfully
+exit(0)
diff --git a/scripts/run-initial-setup b/scripts/run-initial-setup
new file mode 100755
index 0000000..c7e5829
--- /dev/null
+++ b/scripts/run-initial-setup
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+IS_TEXT="/usr/libexec/initial-setup/initial-setup-text"
+IS_GRAPHICAL="/usr/libexec/initial-setup/initial-setup-graphical"
+IS_UNIT=initial-setup.service
+
+WINDOWMANAGER_SCRIPT="/usr/libexec/initial-setup/firstboot-windowmanager"
+
+START_GUI_COMMAND="/bin/xinit ${WINDOWMANAGER_SCRIPT} ${IS_GRAPHICAL} --no-stdout-log -- /bin/Xorg :9 -ac -nolisten tcp"
+
+# check if graphical Initial Setup is installed
+if [ -f ${IS_GRAPHICAL} ]; then
+    printf "MESSAGE=starting Initial Setup GUI\nPRIORITY=6" | logger --journald
+    ${START_GUI_COMMAND}
+else
+    printf "MESSAGE=starting Initial Setup TUI\nPRIORITY=6" | logger --journald
+    ${IS_TEXT} --no-stdout-log
+fi
+
+# check if the Initial Setup run was successfull by looking at the return code
+if [ $? -eq 0 ]; then
+    printf "MESSAGE=Initial Setup finished successfully, disabling\nPRIORITY=6" | logger --journald
+    /bin/systemctl disable initial-setup-graphical.service initial-setup-text.service &>/dev/null
+else
+    printf "MESSAGE=Initial Setup failed, keeping enabled\nPRIORITY=3" | logger --journald
+fi
diff --git a/scripts/s390/initial-setup.csh b/scripts/s390/initial-setup.csh
index 673c9d4..34b1365 100644
--- a/scripts/s390/initial-setup.csh
+++ b/scripts/s390/initial-setup.csh
@@ -1,7 +1,7 @@
 # initial-setup.csh
 
-set IS_EXEC = /usr/bin/initial-setup
-set IS_UNIT = initial-setup-text.service
+set IS_EXEC = /lib/exec/initial-setup-text
+set IS_UNIT = initial-setup.service
 
 # check if the Initial Setup unit is enabled and the executable is available
 if ( { systemctl -q is-enabled $IS_UNIT } && -x $IS_EXEC ) then
diff --git a/scripts/s390/initial-setup.sh b/scripts/s390/initial-setup.sh
index f7bd0cb..4a2f495 100644
--- a/scripts/s390/initial-setup.sh
+++ b/scripts/s390/initial-setup.sh
@@ -1,7 +1,7 @@
 # firstboot.sh
 
-IS_EXEC=/usr/bin/initial-setup
-IS_UNIT=initial-setup-text.service
+IS_EXEC=/lib/exec/initial-setup-text
+IS_UNIT=initial-setup.service
 
 IS_AVAILABLE=0
 # check if the Initial Setup unit is enabled and the executable is available
diff --git a/setup.py b/setup.py
index cbe0b97..16b029d 100644
--- a/setup.py
+++ b/setup.py
@@ -32,7 +32,10 @@ def read(fname):
     return open(os.path.join(os.path.dirname(__file__), fname)).read()
 
 
-data_files = [('/usr/lib/systemd/system', glob('systemd/*.service'))]
+data_files = [('/usr/lib/systemd/system', glob('systemd/*.service')),
+              ('/usr/libexec/initial-setup/',
+              ["scripts/run-initial-setup", "scripts/firstboot-windowmanager",
+               "scripts/initial-setup-text", "scripts/initial-setup-graphical"])]
 
 # add the firstboot start script for s390 architectures
 if os.uname()[4].startswith('s390'):
@@ -52,7 +55,6 @@ setup(
     package_data = {
         "": ["*.glade"]
     },
-    scripts = ["initial-setup", "firstboot-windowmanager"],
     data_files = data_files,
     setup_requires= ['nose>=1.0'],
     test_suite = "initial_setup",
diff --git a/systemd/initial-setup-graphical.service b/systemd/initial-setup-graphical.service
deleted file mode 100644
index 3e55cd1..0000000
--- a/systemd/initial-setup-graphical.service
+++ /dev/null
@@ -1,17 +0,0 @@
-[Unit]
-Description=Initial Setup configuration program
-After=livesys.service plymouth-quit-wait.service
-Before=display-manager.service getty at tty1.service
-Conflicts=plymouth-quit-wait.service initial-setup-text.service
-ConditionKernelCommandLine=!rd.live.image
-
-[Service]
-Type=oneshot
-ExecStartPre=-/bin/plymouth quit
-ExecStart=/bin/xinit /bin/firstboot-windowmanager /bin/initial-setup -- /bin/Xorg :9 -ac -nolisten tcp
-TimeoutSec=0
-RemainAfterExit=no
-SysVStartPriority=99
-
-[Install]
-WantedBy=graphical.target
diff --git a/systemd/initial-setup-text.service b/systemd/initial-setup-text.service
deleted file mode 100644
index 1445b3d..0000000
--- a/systemd/initial-setup-text.service
+++ /dev/null
@@ -1,26 +0,0 @@
-[Unit]
-Description=Initial Setup configuration program (text mode)
-After=livesys.service plymouth-quit-wait.service
-After=systemd-vconsole-setup.service
-Before=display-manager.service getty at tty1.service getty at ttyUSB0.service
-Before=serial-getty at ttyS0.service serial-getty at ttyO0.service serial-getty at ttyO2.service
-Before=serial-getty at ttyAMA0.service serial-getty at ttymxc0.service serial-getty at ttymxc3.service serial-getty at hvc0.service
-Conflicts=plymouth-quit-wait.service
-Conflicts=initial-setup-graphical.service
-After=initial-setup-graphical.service
-ConditionKernelCommandLine=!rd.live.image
-
-[Service]
-Type=oneshot
-ExecStartPre=-/bin/plymouth quit
-ExecStartPre=/bin/kill -55 1
-ExecStart=/bin/initial-setup
-ExecStartPost=/bin/kill -54 1
-TimeoutSec=0
-RemainAfterExit=yes
-SysVStartPriority=99
-StandardInput=tty
-StandardOutput=tty
-
-[Install]
-WantedBy=multi-user.target
diff --git a/systemd/initial-setup.service b/systemd/initial-setup.service
new file mode 100644
index 0000000..c3d7212
--- /dev/null
+++ b/systemd/initial-setup.service
@@ -0,0 +1,26 @@
+[Unit]
+Description=Initial Setup configuration program
+After=livesys.service plymouth-quit-wait.service
+After=systemd-vconsole-setup.service
+Before=display-manager.service getty at tty1.service getty at ttyUSB0.service
+Before=serial-getty at ttyS0.service serial-getty at ttyO0.service serial-getty at ttyO2.service
+Before=serial-getty at ttyAMA0.service serial-getty at ttymxc0.service serial-getty at ttymxc3.service serial-getty at hvc0.service
+Conflicts=plymouth-quit-wait.service initial-setup-text.service initial-setup-graphical.service
+ConditionKernelCommandLine=!rd.live.image
+
+[Service]
+Type=oneshot
+TimeoutSec=0
+StandardInput=tty
+StandardOutput=tty
+RemainAfterExit=yes
+ExecStartPre=/bin/kill -55 1
+ExecStartPre=-/bin/plymouth quit
+ExecStart=/usr/libexec/initial-setup/run-initial-setup
+ExecStartPost=/bin/kill -54 1
+TimeoutSec=0
+RemainAfterExit=no
+
+[Install]
+WantedBy=graphical.target
+WantedBy=multi-user.target
-- 
2.5.0



More information about the anaconda-patches mailing list