普通文本  |  67行  |  2.67 KB

# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import collections
import time

from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib import utils

# Use this with ProcessWatcher to start your process in a minijail.  This
# is useful for instance if you would like to drop autotest's default root
# priviledges.  Both fields must be set to valid users/groups.
MinijailConfig = collections.namedtuple('MinijailConfig', ['user', 'group'])


class ProcessWatcher(object):
    """Start a process, and terminate it later."""

    def __init__(self, command, args=tuple(), minijail_config=None, host=None):
        """Construst a ProcessWatcher without starting the process.

        @param command: string command to use to start the process.
        @param args: list of strings to pass to the command.
        @param minijail_config: MinijailConfig tuple defined above.
        @param host: host object if the server should be started on a remote
                host.

        """
        self._command = ' '.join([command] + list(args))
        if '"' in self._command:
            raise error.TestError('Please implement shell escaping in '
                                  'ProcessWatcher.')
        self._minijail_config = minijail_config
        self._run = utils.run if host is None else host.run


    def start(self):
        """Start a (potentially remote) instance of the process."""
        command = self._command
        prefix = ''
        if self._minijail_config is not None:
            prefix = 'minijail0 -i -g %s -u %s ' % (self._minijail_config.group,
                                                    self._minijail_config.user)
        # Redirect output streams to avoid odd interactions between autotest's
        # shell environment and the command's runtime environment.
        self._run('%s%s >/dev/null 2>&1 &' % (prefix, self._command))


    def close(self, timeout_seconds=40):
        """Close the (potentially remote) instance of the process.

        @param timeout_seconds: int number of seconds to wait for shutdown.

        """
        self._run('pkill -f --signal TERM "%s"' % self._command,
                  ignore_status=True)
        start_time = time.time()
        while time.time() - start_time < timeout_seconds:
            result = self._run('pgrep -f -l "%s"' % self._command,
                               ignore_status=True)
            if result.exit_status != 0:
                return
            time.sleep(0.3)
        raise error.TestError('Timed out waiting for %s to die.' %
                              self._command)