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