# # Functions to launch and test DBus session. # # Functions here are NOT RECOMMENDED to be called in modules. These functions # could have unexpected behavior when called in modules and not in main # anaconda application. # # Copyright (C) 2018 # Red Hat, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Author(s): Jiri Konecny # import os import signal from subprocess import TimeoutExpired from pyanaconda.core.configuration.anaconda import conf from pyanaconda.core.util import startProgram from pyanaconda.core.constants import ANACONDA_BUS_ADDR_FILE, ANACONDA_CONFIG_TMP, \ ANACONDA_BUS_CONF_FILE, DBUS_ANACONDA_SESSION_ADDRESS from pyanaconda.core.dbus import DBus from pyanaconda.core.path import open_with_perm from dasbus.constants import DBUS_FLAG_NONE from pyanaconda.modules.common.constants.services import BOSS from pyanaconda.anaconda_loggers import get_anaconda_root_logger from pyanaconda.modules.common.task import sync_run_task log = get_anaconda_root_logger() __all__ = ["AnacondaDBusLauncher"] class AnacondaDBusLauncher(object): """Class for launching the Anaconda DBus modules.""" DBUS_LAUNCH_BIN = "dbus-daemon" def __init__(self): self._dbus_daemon_process = None self._log_file = None self._bus_address = None @property def bus_address(self): """The address of the Anaconda DBus session.""" return self._bus_address def start(self): """Start DBus modules. Start the DBus session, the boss and the kickstart modules. """ self._write_temporary_config() self._start_dbus_session() self._set_environment() self._write_bus_address() self._start_boss() self._start_modules() def stop(self, timeout=20): """Stop the DBus modules. Stop the DBus session, the boss and the kickstart modules. :param timeout: seconds to the launcher timeout """ self._stop_boss_and_modules() self._stop_dbus_session(timeout) self._remove_bus_address_file() self._remove_temporary_config() def _write_temporary_config(self): """Create the temporary config file.""" dirname = os.path.dirname(ANACONDA_CONFIG_TMP) if not os.path.exists(dirname): os.makedirs(dirname) log.info("Configuration loaded from: %s", conf.get_sources()) log.info("Writing the runtime configuration to: %s", ANACONDA_CONFIG_TMP) conf.write(ANACONDA_CONFIG_TMP) def _remove_temporary_config(self): """Remove the temporary config file.""" if os.path.exists(ANACONDA_CONFIG_TMP): os.unlink(ANACONDA_CONFIG_TMP) def _start_dbus_session(self): """Start dbus session if not running already.""" command = [ self.DBUS_LAUNCH_BIN, '--print-address', "--syslog", "--config-file={}".format(ANACONDA_BUS_CONF_FILE) ] def dbus_preexec(): # to set dbus subprocess SIGINT handler signal.signal(signal.SIGINT, signal.SIG_IGN) self._log_file = open_with_perm('/tmp/dbus.log', 'a', 0o600) self._dbus_daemon_process = startProgram(command, stderr=self._log_file, reset_lang=False, preexec_fn=dbus_preexec) if self._dbus_daemon_process.poll() is not None: raise RuntimeError("DBus wasn't properly started!") address = self._dbus_daemon_process.stdout.readline().decode('utf-8').strip() if not address: raise RuntimeError("Unable to start DBus session!") self._bus_address = address def _stop_dbus_session(self, timeout): """Stop DBus service and clean bus address file.""" if self._log_file: self._log_file.close() if not self._dbus_daemon_process: return self._dbus_daemon_process.terminate() try: self._dbus_daemon_process.wait(timeout) except TimeoutExpired: log.error("DBus daemon wasn't terminated kill it now") self._dbus_daemon_process.kill() ret_code = self._dbus_daemon_process.poll() if ret_code is None: log.error("DBus daemon can't be killed!") elif ret_code != 0: log.error("DBus daemon exited with error %s", ret_code) def _set_environment(self): """Set the environment variables.""" # pylint: disable=environment-modify os.environ[DBUS_ANACONDA_SESSION_ADDRESS] = self._bus_address def _write_bus_address(self): """Write the bus address to a file.""" file_name = ANACONDA_BUS_ADDR_FILE run_dir = os.path.dirname(file_name) if not os.path.exists(run_dir): os.mkdir(run_dir) with open(file_name, 'wt') as f: f.write(self._bus_address) def _remove_bus_address_file(self): """Remove the file with the bus address.""" f = ANACONDA_BUS_ADDR_FILE if os.path.exists(f): os.unlink(f) def _start_boss(self): """Start the boss.""" bus_proxy = DBus.proxy bus_proxy.StartServiceByName(BOSS.service_name, DBUS_FLAG_NONE) def _start_modules(self): """Start the kickstart modules.""" boss_proxy = BOSS.get_proxy() task_path = boss_proxy.StartModulesWithTask() task_proxy = BOSS.get_proxy(task_path) sync_run_task(task_proxy) def _stop_boss_and_modules(self): """Stop the boss and the kickstart modules.""" boss_proxy = BOSS.get_proxy() boss_proxy.Quit()