Home | History | Annotate | Download | only in cros
      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 
      5 import logging
      6 import os
      7 import subprocess
      8 
      9 from autotest_lib.client.bin import utils
     10 
     11 class 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