普通文本  |  79行  |  2.93 KB

# Copyright (c) 2013 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 logging
import os
import subprocess

from autotest_lib.client.bin import utils

class Tcpdump(object):
    """tcpdump capture process wrapper."""

    def __init__(self, iface, dumpfilename):
        """Launches a tcpdump process on the background.

        @param iface: The name of the interface to listen on.
        @param dumpfilename: The filename of the destination dump file.
        @raise utils.TimeoutError if tcpdump fails to start after 10 seconds.
        """
        logging.debug('Recording %s traffic to %s.', iface, dumpfilename)
        # Force to run tcpdump as root, since the dump file is created *after*
        # the process drops to a unprivileged user, meaning that it can't create
        # the passed dumpfilename file.
        self._tcpdump_proc = subprocess.Popen(
                ['tcpdump', '-i', iface, '-w', dumpfilename, '-Z', 'root'],
                stdout=open('/dev/null', 'w'),
                stderr=subprocess.STDOUT)
        # Wait for tcpdump to initialize and create the dump file.
        utils.poll_for_condition(
                lambda: os.path.exists(dumpfilename),
                desc='tcpdump creates the dump file.',
                sleep_interval=1,
                timeout=10.)


    def stop(self, timeout=10.):
        """Stop the dump process and wait for it to return.

        This method stops the tcpdump process running in background and waits
        for it to finish for a given timeout.
        @param timeout: The time to wait for the tcpdump to finish in seconds.
                        None means no timeout.
        @return whether the tcpdump is not running.
        """
        if not self._tcpdump_proc:
            return True

        # Send SIGTERM to tcpdump.
        try:
            self._tcpdump_proc.terminate()
        except OSError, e:
            # If the process exits before we can send it a SIGTERM, an
            # OSError exception is raised here which we can ignore since the
            # process already finished.
            logging.error('Trying to kill tcpdump (%d): %s',
                          self._tcpdump_proc.pid, e.strerror)

        logging.debug('Waiting for pid %d to finish.', self._tcpdump_proc.pid)
        if timeout is None:
            self._tcpdump_proc.wait()
        else:
            try:
                utils.poll_for_condition(
                        lambda: not self._tcpdump_proc.poll() is None,
                        sleep_interval=1,
                        timeout=timeout)
            except utils.TimeoutError:
                logging.error('tcpdump failed to finish after %f seconds. Dump '
                              'file can be truncated.', timeout)
                return False

        self._tcpdump_proc = None
        return True


    def __del__(self):
        self.stop()