Home | History | Annotate | Download | only in cros
      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