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