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