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