1# Copyright 2014 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 gzip, logging, os, re 6from autotest_lib.client.bin import utils 7from autotest_lib.client.common_lib import error 8 9class KernelConfig(): 10 """ 11 Parse the kernel config and enable us to query it. 12 Used to verify the kernel config (see kernel_ConfigVerify). 13 """ 14 15 def _passed(self, msg): 16 logging.info('ok: %s', msg) 17 18 def _failed(self, msg): 19 logging.error('FAIL: %s', msg) 20 self._failures.append(msg) 21 22 def failures(self): 23 """Return the list of failures that occured during the test. 24 25 @return a list of string describing errors that occured since 26 initialization. 27 """ 28 return self._failures 29 30 def _fatal(self, msg): 31 logging.error('FATAL: %s', msg) 32 raise error.TestError(msg) 33 34 def get(self, key, default): 35 """Get the value associated to key or default if it does not exist 36 37 @param key: key to look for. 38 @param default: value returned if key is not set in self._config 39 """ 40 return self._config.get(key, default) 41 42 def _config_required(self, name, wanted): 43 value = self._config.get(name, None) 44 if value in wanted: 45 self._passed('"%s" was "%s" in kernel config' % (name, value)) 46 else: 47 states = [] 48 for state in wanted: 49 if state == None: 50 states.append("unset") 51 else: 52 states.append(state) 53 self._failed('"%s" was "%s" (wanted one of "%s") in kernel config' % 54 (name, value, '|'.join(states))) 55 56 def has_value(self, name, value): 57 """Determine if the name config item has a specific value. 58 59 @param name: name of config item to test 60 @param value: value expected for the given config name 61 """ 62 self._config_required('CONFIG_%s' % (name), value) 63 64 def has_builtin(self, name): 65 """Check if the specific config item is built-in (present but not 66 built as a module). 67 68 @param name: name of config item to test 69 """ 70 wanted = ['y'] 71 if name in self._missing_ok: 72 wanted.append(None) 73 self.has_value(name, wanted) 74 75 def has_module(self, name): 76 """Check if the specific config item is a module (present but not 77 built-in). 78 79 @param name: name of config item to test 80 """ 81 wanted = ['m'] 82 if name in self._missing_ok: 83 wanted.append(None) 84 self.has_value(name, wanted) 85 86 def is_enabled(self, name): 87 """Check if the specific config item is present (either built-in or 88 a module). 89 90 @param name: name of config item to test 91 """ 92 wanted = ['y', 'm'] 93 if name in self._missing_ok: 94 wanted.append(None) 95 self.has_value(name, wanted) 96 97 def is_missing(self, name): 98 """Check if the specific config item is not present (neither built-in 99 nor a module). 100 101 @param name: name of config item to test 102 """ 103 self.has_value(name, [None]) 104 105 def is_exclusive(self, exclusive): 106 """Given a config item regex, make sure only the expected items 107 are present in the kernel configs. 108 109 @param exclusive: hash containing "missing", "builtin", "module", 110 "enabled" each to be checked with the corresponding 111 has_* function based on config items matching the 112 "regex" value. 113 """ 114 expected = set() 115 for name in exclusive['missing']: 116 self.is_missing(name) 117 for name in exclusive['builtin']: 118 self.has_builtin(name) 119 expected.add('CONFIG_%s' % (name)) 120 for name in exclusive['module']: 121 self.has_module(name) 122 expected.add('CONFIG_%s' % (name)) 123 for name in exclusive['enabled']: 124 self.is_enabled(name) 125 expected.add('CONFIG_%s' % (name)) 126 127 # Now make sure nothing else with the specified regex exists. 128 regex = r'CONFIG_%s' % (exclusive['regex']) 129 for name in self._config: 130 if not re.match(regex, name): 131 continue 132 if not name in expected: 133 self._failed('"%s" found for "%s" when only "%s" allowed' % 134 (name, regex, "|".join(expected))) 135 136 def _open_config(self): 137 """Open the kernel's build config file. Attempt to use the built-in 138 symbols from /proc first, then fall back to looking for a text file 139 in /boot. 140 141 @return fileobj for open config file 142 """ 143 filename = '/proc/config.gz' 144 if not os.path.exists(filename): 145 utils.system("modprobe configs", ignore_status=True) 146 if os.path.exists(filename): 147 return gzip.open(filename, "r") 148 149 filename = '/boot/config-%s' % utils.system_output('uname -r') 150 if os.path.exists(filename): 151 logging.info('Falling back to reading %s', filename) 152 return file(filename, "r") 153 154 self._fatal("Cannot locate suitable kernel config file") 155 156 def initialize(self, missing_ok=None): 157 """Load the kernel configuration and parse it. 158 """ 159 fileobj = self._open_config() 160 # Import kernel config variables into a dictionary for each searching. 161 config = dict() 162 for item in fileobj.readlines(): 163 item = item.strip() 164 if not '=' in item: 165 continue 166 key, value = item.split('=', 1) 167 config[key] = value 168 169 # Make sure we actually loaded something sensible. 170 if len(config) == 0: 171 self._fatal('No CONFIG variables found!') 172 173 self._config = config 174 self._failures = [] 175 self._missing_ok = set() 176 if missing_ok: 177 self._missing_ok |= set(missing_ok) 178