blob: 3ccfefa9d7de499effae900285d42942db5b62c8 [file] [log] [blame]
# 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)