1# Copyright 2018 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import subprocess 6 7# Full path of the CUPS configuration file 8_CUPS_CONF_FILE = '/etc/cups/cupsd.conf' 9 10class CommandFailedException(Exception): 11 """ 12 An simple exception that is thrown when OS command fails. 13 14 """ 15 pass 16 17class Configurator(): 18 """ 19 An instance of this class is responsible for an initial configuration of 20 the system. This is performed by the method configure(). To restore the 21 system to the state before a configure() call, the method restore() must 22 be called. 23 24 """ 25 26 def __init__(self): 27 """ 28 Constructor. 29 30 """ 31 self._cupsd_conf_loglevel_line_no = None 32 self._cupsd_conf_loglevel_content = None 33 34 35 def _run_as_root(self, argv): 36 """ 37 Run given command as root. 38 39 @param argv: an array of command-line parameters 40 41 @returns standard output produced by the command 42 43 @raises Exception if the command returns code different than 0 44 45 """ 46 p1 = subprocess.Popen(["echo", "test0000"], stdout=subprocess.PIPE) 47 p2 = subprocess.Popen(["sudo", "--stdin", "--prompt="] + argv, 48 stdin=p1.stdout, stdout=subprocess.PIPE) 49 p1.stdout.close() 50 out,err = p2.communicate() 51 if p2.returncode != 0: 52 raise CommandFailedException("The command '%s' returns %d" % 53 (' '.join(argv),p2.returncode)); 54 return out 55 56 57 def _set_cups_logging_level(self): 58 """ 59 Modify the CUPS configuration file to set log level to 'debug'. 60 61 @raises Exception in case of any errors 62 63 """ 64 # parse content of the CUPS configuration file and find a number of 65 # a line with 'LogLevel' option 66 lines = self._run_as_root(["cat", _CUPS_CONF_FILE]).splitlines() 67 68 line_no = None 69 for index, line in enumerate(lines): 70 if line.startswith(b'LogLevel'): 71 line_no = index 72 break 73 if line_no is None: 74 raise Exception('Cannot find a line with LogLevel in cupsd.conf') 75 # save the original line and replace it with 'LogLevel debug' 76 self._cupsd_conf_loglevel_content = lines[line_no] 77 self._cupsd_conf_loglevel_line_no = line_no + 1 78 self._run_as_root(['sed', '-i', '%ds/.*/LogLevel debug/' % (line_no+1), 79 _CUPS_CONF_FILE]) 80 # if CUPS is started, we have to stop 81 try: 82 self._run_as_root(['stop', 'cupsd']) 83 except CommandFailedException: 84 pass 85 86 87 def _restore_cups_logging_level(self): 88 """ 89 Restore content of the CUPS configuration file to this one before 90 calling _set_cups_logging_level(). Do nothing if the method 91 _set_cups_logging_level() was not called earlier. 92 93 """ 94 if self._cupsd_conf_loglevel_content is None: 95 return 96 self._run_as_root(['sed', '-i', '%ds/.*/%s/' % 97 (self._cupsd_conf_loglevel_line_no, 98 self._cupsd_conf_loglevel_content), _CUPS_CONF_FILE]) 99 self._cupsd_conf_loglevel_content = None 100 self._cupsd_conf_loglevel_line_no = None 101 102 103 def _set_root_partition_as_read_write(self): 104 """ 105 Remount the root partition in read-write mode. 106 107 """ 108 self._run_as_root(['mount', '-o', 'rw,remount', '/']) 109 110 111 def configure(self, set_cups_logging_level): 112 """ 113 Apply the configuration required by the test. 114 115 @param set_cups_logging_level: True or False; if True then 116 the root partition is remounted in R/W mode and the CUPS 117 configuration file is updated to set "LogLevel" to "debug". 118 """ 119 # Update CUPS logging level 120 if set_cups_logging_level: 121 self._set_root_partition_as_read_write() 122 self._set_cups_logging_level() 123 124 125 def restore(self): 126 """ 127 Restore the system state before configure(). It is safe to run 128 this method, even if configure() failed or has not been called. 129 130 """ 131 # Restore CUPS logging level 132 if self._cupsd_conf_loglevel_content is not None: 133 self._restore_cups_logging_level() 134