| # Copyright 2018 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import dbus |
| import subprocess |
| |
| from autotest_lib.client.common_lib.cros import dbus_send |
| |
| # Full path of the CUPS configuration file |
| _CUPS_CONF_FILE = '/etc/cups/cupsd.conf' |
| |
| class Configurator(): |
| """ |
| An instance of this class is responsible for an initial configuration of |
| the system. This is performed by the method configure(). To restore the |
| system to the state before a configure() call, the method restore() must |
| be called. |
| |
| """ |
| |
| def __init__(self): |
| """ |
| Constructor. |
| |
| """ |
| self._cupsd_conf_loglevel_line_no = None |
| self._cupsd_conf_loglevel_content = None |
| self._loaded_components = [] |
| self._components_to_load = ['epson-inkjet-printer-escpr', |
| 'star-cups-driver'] |
| |
| |
| def _load_component(self, component): |
| """ |
| Download filter component via dbus API |
| |
| @param component: name of component |
| |
| @raises Exception if component is not loaded. |
| |
| """ |
| if component in self._loaded_components: |
| return |
| res = dbus_send.dbus_send( |
| 'org.chromium.ComponentUpdaterService', |
| 'org.chromium.ComponentUpdaterService', |
| '/org/chromium/ComponentUpdaterService', |
| 'LoadComponent', |
| timeout_seconds=20, |
| user='root', |
| args=[dbus.String(component)]) |
| if res.response == '': |
| message = 'Component %s could not be loaded.' % component |
| raise Exception(message) |
| self._loaded_components.append(component) |
| |
| |
| def _delete_component(self, component): |
| """ |
| Delete filter component via dbus API |
| |
| @param component: name of component |
| |
| """ |
| if component not in self._loaded_components: |
| return |
| dbus_send.dbus_send( |
| 'org.chromium.ComponentUpdaterService', |
| 'org.chromium.ComponentUpdaterService', |
| '/org/chromium/ComponentUpdaterService', |
| 'UnloadComponent', |
| timeout_seconds=20, |
| user='root', |
| args=[dbus.String(component)]) |
| self._loaded_components.remove(component) |
| |
| |
| def _run_as_root(self, argv): |
| """ |
| Run given command as root. |
| |
| @param argv: an array of command-line parameters |
| |
| @returns standard output produced by the command |
| |
| @raises Exception if the command returns code different than 0 |
| |
| """ |
| p1 = subprocess.Popen(["echo", "test0000"], stdout=subprocess.PIPE) |
| p2 = subprocess.Popen(["sudo", "--stdin", "--prompt="] + argv, |
| stdin=p1.stdout, stdout=subprocess.PIPE) |
| p1.stdout.close() |
| out,err = p2.communicate() |
| if p2.returncode != 0: |
| raise Exception("The command '%s' returns %d" % |
| (' '.join(argv),p2.returncode)); |
| return out |
| |
| |
| def _set_cups_logging_level(self): |
| """ |
| Modify the CUPS configuration file to set log level to 'debug'. |
| |
| @raises Exception in case of any errors |
| |
| """ |
| # parse content of the CUPS configuration file and find a number of |
| # a line with 'LogLevel' option |
| lines = self._run_as_root(["cat", _CUPS_CONF_FILE]).splitlines() |
| |
| for index, line in enumerate(lines): |
| if line.startswith('LogLevel'): |
| line_no = index |
| break |
| if line_no is None: |
| raise Exception('Cannot find a line with LogLevel in cupsd.conf') |
| # save the original line and replace it with 'LogLevel debug' |
| self._cupsd_conf_loglevel_content = lines[line_no] |
| self._cupsd_conf_loglevel_line_no = line_no + 1 |
| self._run_as_root(['sed', '-i', '%ds/.*/LogLevel debug/' % (line_no+1), |
| _CUPS_CONF_FILE]) |
| # if CUPS is started, we have to stop |
| try: |
| self._run_as_root(['stop', 'cupsd']) |
| except: |
| None |
| |
| |
| def _restore_cups_logging_level(self): |
| """ |
| Restore content of the CUPS configuration file to this one before |
| calling _set_cups_logging_level(). Do nothing if the method |
| _set_cups_logging_level() was not called earlier. |
| |
| """ |
| if self._cupsd_conf_loglevel_content is None: |
| return |
| self._run_as_root(['sed', '-i', '%ds/.*/%s/' % |
| (self._cupsd_conf_loglevel_line_no, |
| self._cupsd_conf_loglevel_content), _CUPS_CONF_FILE]) |
| self._cupsd_conf_loglevel_content = None |
| self._cupsd_conf_loglevel_line_no = None |
| |
| |
| def _set_root_partition_as_read_write(self): |
| """ |
| Remount the root partition in read-write mode. |
| |
| """ |
| self._run_as_root(['mount', '-o', 'rw,remount', '/']) |
| |
| |
| def _set_root_partition_as_read_only(self): |
| """ |
| Remount the root partition in read-only mode. |
| |
| """ |
| self._run_as_root(['mount', '-o', 'ro,remount', '/']) |
| |
| |
| def configure(self, set_cups_logging_level): |
| """ |
| Apply the configuration required by the test. |
| |
| @param set_cups_logging_level: True or False; if True then |
| the root partition is remounted in R/W mode and the CUPS |
| configuration file is updated to set "LogLevel" to "debug". |
| """ |
| # Load components required by some printers (Epson and Star) |
| for component in self._components_to_load: |
| self._load_component(component) |
| # Update CUPS logging level |
| if set_cups_logging_level: |
| self._set_root_partition_as_read_write() |
| self._set_cups_logging_level() |
| self._set_root_partition_as_read_only() |
| |
| |
| def restore(self): |
| """ |
| Restore the system state before configure(). It is safe to run |
| this method, even if configure() failed or has not been called. |
| |
| """ |
| # Restore CUPS logging level |
| if self._cupsd_conf_loglevel_content is not None: |
| self._set_root_partition_as_read_write() |
| self._restore_cups_logging_level() |
| self._set_root_partition_as_read_write() |
| # Deleted components loaded during initialization |
| for component in self._components_to_load: |
| self._delete_component(component) |