• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Lint as: python2, python3
2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import logging
7import os
8import subprocess
9
10from autotest_lib.client.bin import utils
11
12class Tcpdump(object):
13    """tcpdump capture process wrapper."""
14
15    def __init__(self, iface, dumpfilename):
16        """Launches a tcpdump process on the background.
17
18        @param iface: The name of the interface to listen on.
19        @param dumpfilename: The filename of the destination dump file.
20        @raise utils.TimeoutError if tcpdump fails to start after 10 seconds.
21        """
22        logging.debug('Recording %s traffic to %s.', iface, dumpfilename)
23        # Force to run tcpdump as root, since the dump file is created *after*
24        # the process drops to a unprivileged user, meaning that it can't create
25        # the passed dumpfilename file.
26        self._tcpdump_proc = subprocess.Popen(
27                ['tcpdump', '-i', iface, '-w', dumpfilename, '-Z', 'root'],
28                stdout=open('/dev/null', 'w'),
29                stderr=subprocess.STDOUT)
30        # Wait for tcpdump to initialize and create the dump file.
31        utils.poll_for_condition(
32                lambda: os.path.exists(dumpfilename),
33                desc='tcpdump creates the dump file.',
34                sleep_interval=1,
35                timeout=10.)
36
37
38    def stop(self, timeout=10.):
39        """Stop the dump process and wait for it to return.
40
41        This method stops the tcpdump process running in background and waits
42        for it to finish for a given timeout.
43        @param timeout: The time to wait for the tcpdump to finish in seconds.
44                        None means no timeout.
45        @return whether the tcpdump is not running.
46        """
47        if not self._tcpdump_proc:
48            return True
49
50        # Send SIGTERM to tcpdump.
51        try:
52            self._tcpdump_proc.terminate()
53        except OSError as e:
54            # If the process exits before we can send it a SIGTERM, an
55            # OSError exception is raised here which we can ignore since the
56            # process already finished.
57            logging.error('Trying to kill tcpdump (%d): %s',
58                          self._tcpdump_proc.pid, e.strerror)
59
60        logging.debug('Waiting for pid %d to finish.', self._tcpdump_proc.pid)
61        if timeout is None:
62            self._tcpdump_proc.wait()
63        else:
64            try:
65                utils.poll_for_condition(
66                        lambda: not self._tcpdump_proc.poll() is None,
67                        sleep_interval=1,
68                        timeout=timeout)
69            except utils.TimeoutError:
70                logging.error('tcpdump failed to finish after %f seconds. Dump '
71                              'file can be truncated.', timeout)
72                return False
73
74        self._tcpdump_proc = None
75        return True
76
77
78    def __del__(self):
79        self.stop()
80