# 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

from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib import process_utils
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.

        """
        process_utils.pkill_process(self._command,
                                    timeout_seconds=timeout_seconds)