blob: 9aa503bee48f403344ec71d98e497c9670d39924 [file] [log] [blame]
# Lint as: python2, python3
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import logging
import os
import subprocess
from autotest_lib.client.bin import utils
class Tcpdump(object):
"""tcpdump capture process wrapper."""
def __init__(self, iface, dumpfilename):
"""Launches a tcpdump process on the background.
@param iface: The name of the interface to listen on.
@param dumpfilename: The filename of the destination dump file.
@raise utils.TimeoutError if tcpdump fails to start after 10 seconds.
"""
logging.debug('Recording %s traffic to %s.', iface, dumpfilename)
# Force to run tcpdump as root, since the dump file is created *after*
# the process drops to a unprivileged user, meaning that it can't create
# the passed dumpfilename file.
self._tcpdump_proc = subprocess.Popen(
['tcpdump', '-i', iface, '-w', dumpfilename, '-Z', 'root'],
stdout=open('/dev/null', 'w'),
stderr=subprocess.STDOUT)
# Wait for tcpdump to initialize and create the dump file.
utils.poll_for_condition(
lambda: os.path.exists(dumpfilename),
desc='tcpdump creates the dump file.',
sleep_interval=1,
timeout=10.)
def stop(self, timeout=10.):
"""Stop the dump process and wait for it to return.
This method stops the tcpdump process running in background and waits
for it to finish for a given timeout.
@param timeout: The time to wait for the tcpdump to finish in seconds.
None means no timeout.
@return whether the tcpdump is not running.
"""
if not self._tcpdump_proc:
return True
# Send SIGTERM to tcpdump.
try:
self._tcpdump_proc.terminate()
except OSError as e:
# If the process exits before we can send it a SIGTERM, an
# OSError exception is raised here which we can ignore since the
# process already finished.
logging.error('Trying to kill tcpdump (%d): %s',
self._tcpdump_proc.pid, e.strerror)
logging.debug('Waiting for pid %d to finish.', self._tcpdump_proc.pid)
if timeout is None:
self._tcpdump_proc.wait()
else:
try:
utils.poll_for_condition(
lambda: not self._tcpdump_proc.poll() is None,
sleep_interval=1,
timeout=timeout)
except utils.TimeoutError:
logging.error('tcpdump failed to finish after %f seconds. Dump '
'file can be truncated.', timeout)
return False
self._tcpdump_proc = None
return True
def __del__(self):
self.stop()