1f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso# Copyright 2017 The Chromium OS Authors. All rights reserved. 2f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso# Use of this source code is governed by a BSD-style license that can be 3f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso# found in the LICENSE file. 4f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 5f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport logging 6f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport os 7f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport pyudev 8f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport re 9f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport select 10f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport struct 11f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport subprocess 12f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport threading 13f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoimport time 14f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusofrom autotest_lib.client.common_lib import error 15f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 16f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 17f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric CarusoJAIL_CONTROL_PATH = '/dev/jail-control' 18f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric CarusoJAIL_REQUEST_PATH = '/dev/jail-request' 19f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 20f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso# From linux/device_jail.h. 21f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric CarusoREQUEST_ALLOW = 0 22f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric CarusoREQUEST_ALLOW_WITH_LOCKDOWN = 1 23f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric CarusoREQUEST_ALLOW_WITH_DETACH = 2 24f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric CarusoREQUEST_DENY = 3 25f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 26f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 27f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoclass OSFile: 28f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso """Simple context manager for file descriptors.""" 29f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __init__(self, path, flag): 30f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._fd = os.open(path, flag) 31f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 32f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def close(self): 33f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso os.close(self._fd) 34f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 35f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __enter__(self): 36f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso """Returns the fd so it can be used in with-blocks.""" 37f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso return self._fd 38f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 39f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __exit__(self, exc_type, exc_val, traceback): 40f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self.close() 41f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 42f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 43f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoclass ConcurrentFunc: 44f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso """Simple context manager that starts and joins a thread.""" 45f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __init__(self, target_func, timeout_func): 46f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._thread = threading.Thread(target=target_func) 47f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._timeout_func = timeout_func 48f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._target_name = target_func.__name__ 49f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 50f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __enter__(self): 51f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._thread.start() 52f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 53f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __exit__(self, exc_type, exc_val, traceback): 54f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._thread.join(self._timeout_func()) 55f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso if self._thread.is_alive() and not exc_val: 56f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso raise error.TestError('Function %s timed out' % self._target_name) 57f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 58f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 59f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusoclass JailDevice: 60f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso TIMEOUT_SEC = 3 61f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso PATH_MAX = 4096 62f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 63f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __init__(self, path_to_jail): 64f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._path_to_jail = path_to_jail 65f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 66f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 67f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __enter__(self): 68f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso """ 69f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso Creates a jail device for the device located at self._path_to_jail. 70f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso If the jail already exists, don't take ownership of it. 71f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso """ 72f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso try: 73f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso output = subprocess.check_output( 74f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso ['device_jail_utility', 75f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso '--add={0}'.format(self._path_to_jail)], 76f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso stderr=subprocess.STDOUT) 77f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 78f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso match = re.search('created jail at (.*)', output) 79f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso if match: 80f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._path = match.group(1) 81f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._owns_device = True 82f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso return self 83f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 84f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso match = re.search('jail already exists at (.*)', output) 85f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso if match: 86f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._path = match.group(1) 87f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso self._owns_device = False 88f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso return self 89f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 90f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso raise error.TestError('Failed to create device jail') 91f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso except subprocess.CalledProcessError as e: 92f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso raise error.TestError('Failed to call device_jail_utility') 93f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 94f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 95f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def expect_open(self, verdict): 96f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso """ 97f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso Tries to open the jail device. This method mocks out the 98f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso device_jail request server which is normally run by permission_broker. 99f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso This allows us to set the verdict we want to test. Since the open 100f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso call will block until we return the verdict, we have to use a 101f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso separate thread to perform the open call, as well. 102f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso """ 103f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # Python 2 does not support "nonlocal" so this closure can't 104f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # set the values of identifiers it closes over unless they 105f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # are in global scope. Work around this by using a list and 106f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # value-mutation. 107f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso dev_file_wrapper = [None] 108f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def open_device(): 109f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso try: 110f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso dev_file_wrapper[0] = OSFile(self._path, os.O_RDWR) 111f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso except OSError as e: 112f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # We don't throw an error because this might be intentional, 113f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # such as when the verdict is REQUEST_DENY. 114f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso logging.info("Failed to open jail device: %s", e.strerror) 115f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 116f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # timeout_sec should be used for the timeouts below. 117f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # This ensures we don't spend much longer than TIMEOUT_SEC in 118f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # this method. 119f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso deadline = time.time() + self.TIMEOUT_SEC 120f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def timeout_sec(): 121f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso return max(deadline - time.time(), 0.01) 122f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 123f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # We have to use FDs because polling works with FDs and 124f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # buffering is silly. 125f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso try: 126f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso req_f = OSFile(JAIL_REQUEST_PATH, os.O_RDWR) 127f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso except OSError as e: 128f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso raise error.TestError( 129f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 'Failed to open request device: %s' % e.strerror) 130f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 131f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso with req_f as req_fd: 132f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso poll_obj = select.poll() 133f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso poll_obj.register(req_fd, select.POLLIN) 134f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 135f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # Starting open_device should ensure we have a request waiting 136f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # on the request device. 137f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso with ConcurrentFunc(open_device, timeout_sec): 138f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso ready_fds = poll_obj.poll(timeout_sec() * 1000) 139f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso if not ready_fds: 140f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso raise error.TestError('Timed out waiting for jail-request') 141f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 142f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso # Sanity check the request. 143f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso path = os.read(req_fd, self.PATH_MAX) 144f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso logging.info('Received jail-request for path %s', path) 145f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso if path != self._path_to_jail: 146f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso raise error.TestError('Got request for the wrong path') 147f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 148f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso os.write(req_fd, struct.pack('I', verdict)) 149f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso logging.info('Responded to jail-request') 150f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 151f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso return dev_file_wrapper[0] 152f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 153f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 154f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso def __exit__(self, exc_type, exc_val, traceback): 155f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso if self._owns_device: 156f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso subprocess.call(['device_jail_utility', 157f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso '--remove={0}'.format(self._path)]) 158f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 159f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso 160f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Carusodef get_usb_devices(): 161f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso context = pyudev.Context() 162f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso return [device for device in context.list_devices() 163f6ead933bd0819f78863988dfdb4dd10c3e92e5bEric Caruso if device.device_node and device.device_node.startswith('/dev/bus/usb')] 164