1 # Copyright 2015 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 5 import collections 6 import time 7 8 from autotest_lib.client.common_lib import error 9 from autotest_lib.client.common_lib import utils 10 11 # Use this with ProcessWatcher to start your process in a minijail. This 12 # is useful for instance if you would like to drop autotest's default root 13 # priviledges. Both fields must be set to valid users/groups. 14 MinijailConfig = collections.namedtuple('MinijailConfig', ['user', 'group']) 15 16 17 class ProcessWatcher(object): 18 """Start a process, and terminate it later.""" 19 20 def __init__(self, command, args=tuple(), minijail_config=None, host=None): 21 """Construst a ProcessWatcher without starting the process. 22 23 @param command: string command to use to start the process. 24 @param args: list of strings to pass to the command. 25 @param minijail_config: MinijailConfig tuple defined above. 26 @param host: host object if the server should be started on a remote 27 host. 28 29 """ 30 self._command = ' '.join([command] + list(args)) 31 if '"' in self._command: 32 raise error.TestError('Please implement shell escaping in ' 33 'ProcessWatcher.') 34 self._minijail_config = minijail_config 35 self._run = utils.run if host is None else host.run 36 37 38 def start(self): 39 """Start a (potentially remote) instance of the process.""" 40 command = self._command 41 prefix = '' 42 if self._minijail_config is not None: 43 prefix = 'minijail0 -i -g %s -u %s ' % (self._minijail_config.group, 44 self._minijail_config.user) 45 # Redirect output streams to avoid odd interactions between autotest's 46 # shell environment and the command's runtime environment. 47 self._run('%s%s >/dev/null 2>&1 &' % (prefix, self._command)) 48 49 50 def close(self, timeout_seconds=40): 51 """Close the (potentially remote) instance of the process. 52 53 @param timeout_seconds: int number of seconds to wait for shutdown. 54 55 """ 56 self._run('pkill -f --signal TERM "%s"' % self._command, 57 ignore_status=True) 58 start_time = time.time() 59 while time.time() - start_time < timeout_seconds: 60 result = self._run('pgrep -f -l "%s"' % self._command, 61 ignore_status=True) 62 if result.exit_status != 0: 63 return 64 time.sleep(0.3) 65 raise error.TestError('Timed out waiting for %s to die.' % 66 self._command) 67