You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
80 lines
3.0 KiB
80 lines
3.0 KiB
# 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()
|