blob: 9aa503bee48f403344ec71d98e497c9670d39924 [file] [log] [blame]
Derek Beckettec417c62020-10-21 12:24:06 -07001# Lint as: python2, python3
Alex Deymoa25ea0d2013-12-27 12:42:34 -08002# 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()
Derek Beckettec417c62020-10-21 12:24:06 -070053 except OSError as e:
Alex Deymoa25ea0d2013-12-27 12:42:34 -080054 # 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()