LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Blogs > konsolebox
User Name
Password

Notices


Rate this Entry

Example script to run and manage a service (tcpdump) with managed logging.

Posted 10-11-2012 at 07:59 PM by konsolebox
Updated 05-27-2018 at 03:10 PM by konsolebox

I made this script as a reply to a question on a thread before. The accidentally simplified complexity of it amazes me so I thought that it would be nice to post this on a blog as well. Original thread: https://www.linuxquestions.org/quest...cpdump-800385/

The script runs and manages a service (tcpdump). It could also automatically delete files older than C days, and reduce the size of a logfile if it's already larger than N bytes.
Code:
#!/bin/bash

# ----------------------------------------------------------------------

# tcpdump-master
#
# The script is a tcpdump service starter and manager.  It can also
# automatically delete files older than C days, and reduce the size of
# the main log file if it's already larger than N bytes.
#
# The script was originally a solution for this thread in LQ:
# https://www.linuxquestions.org/questions/linux-networking-3/rotating-capture-files-using-tcpdump-800385/
#
# Note: It is recommended to use Bash version 4.3 or newer to prevent
# crashes related to race conditions in catching signals and handling
# traps.
#
# Disclaimer: This tool comes with no warranty.
#
# Author: konsolebox
# Copyright Free / Public Domain
# June 13, 2015

# ----------------------------------------------------------------------

if [ -z "${BASH_VERSION}" ]; then
	echo "You need Bash to run this script."
	exit 1
fi

shopt -s extglob

# Settings

LOG_DIR='/var/log/tcpdump'

MAIN_LOG_FILE='main.log'
MAIN_LOG_FILE_MAX_SIZE=$(( 20 * 1024 * 1024 ))  ## Bytes.  File is reduced when this size is reached.
MAIN_LOG_FILE_ALLOWANCE=$(( 1 * 1024 * 1024 ))  ## Bytes.  This is the extra space given when file is reduced.
MAIN_LOG_CHECK_INTERVALS=300                    ## Seconds.  Recommended: >= 300

TCPDUMP='/usr/sbin/tcpdump'
TCPDUMP_ARGS=(-C 1)         ## Customize arguments here e.g. (-C 1 "another with spaces")
TCPDUMP_CAPTURE_FILE_PREFIX='capture-'
TCPDUMP_CAPTURE_FILE_SUFFIX=''
TCPDUMP_CHECK_INTERVALS=60  ## Seconds

OLD=14              ## Days
DD_BLOCK_SIZE=512   ## Bytes
TEMP_DIR='/var/tmp'

# Other runtime variables.  Do not touch.

CURRENT_DATE=''
TCPDUMP_PID=0
QUIT=false

# Functions

function log {
	echo "[$(date '+%F %T')] $1" >> "${MAIN_LOG_FILE}"
	echo "$1"
}

function check_tcpdump {
	[[ ${TCPDUMP_PID} -ne 0 ]] && [[ -e /proc/${TCPDUMP_PID} ]] && kill -s 0 "${TCPDUMP_PID}" 2>/dev/null
}

function start_tcpdump {
	log "Starting tcpdump."

	CURRENT_DATE=$(date +%F)
	local BASENAME=${TCPDUMP_CAPTURE_FILE_PREFIX}${CURRENT_DATE}${TCPDUMP_CAPTURE_FILE_SUFFIX}
	local EXISTING_FILES=()

	{
		if [[ BASH_VERSINFO -ge 4 ]]; then
			readarray -t EXISTING_FILES
		else
			local I=0

			while read -r LINE; do
				EXISTING_FILES[I++]=${LINE}
			done
		fi
	} < <(compgen -G "${LOG_DIR}/${BASENAME}.+([[:digit:]]).log*([[:digit:]])")

	local NEXT_SESSION=0

	if [[ ${#EXISTING_FILES[@]} -gt 0 ]]; then
		local SESSION_NUMBER

		for FILE in "${EXISTING_FILES[@]}"; do
			SESSION_NUMBER=${FILE%.log*}
			SESSION_NUMBER=${SESSION_NUMBER##*.}
			[[ ${SESSION_NUMBER} == +([[:digit:]]) && SESSION_NUMBER -ge NEXT_SESSION ]] && NEXT_SESSION=$(( SESSION_NUMBER + 1 ))
		done
	fi

	local OUTPUT_FILE=${LOG_DIR}/${BASENAME}.${NEXT_SESSION}.log
	"${TCPDUMP}" "${TCPDUMP_ARGS[@]}" -w "${OUTPUT_FILE}" &

	if [[ $? -ne 0 ]]; then
		TCPDUMP_PID=0
		return 1
	fi

	TCPDUMP_PID=$!
	disown "${TCPDUMP_PID}"
	log "PID of tcpdump: ${TCPDUMP_PID}"
	check_tcpdump
}

function start_tcpdump_loop {
	until start_tcpdump; do
		log "Error: Failed to start tcpdump.  Waiting for 20 seconds before next attempt."
		read -t 20

		if [[ ${QUIT} == true ]]; then
			log "Ending tcpdump manager script."
			exit
		fi
	done
}

function stop_tcpdump {
	log "Stopping tcpdump."
	kill "${TCPDUMP_PID}"
	sleep 1
	check_tcpdump && kill -s 9 "${TCPDUMP_PID}"
	TCPDUMP_PID=0
}

function restart_tcpdump {
	log "Restarting tcpdump."
	check_tcpdump && stop_tcpdump
	start_tcpdump_loop
}

function signal_caught_callback {
	local SIGNAL=$1
	log "Caught signal ${SIGNAL}."
	QUIT=true
}

function main {
	local CAPTURE_FILE_PATTERN FILE NEW_DATE SIZE TEMP_FILE I

	CAPTURE_FILE_PATTERN="${TCPDUMP_CAPTURE_FILE_PREFIX}[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]${TCPDUMP_CAPTURE_FILE_SUFFIX}.log*"
	[[ ${MAIN_LOG_FILE} != */* ]] && MAIN_LOG_FILE=${LOG_DIR}/${MAIN_LOG_FILE}

	log "----------------------------------------"
	log "Starting up."
	log "PID: $$"

	[[ ${MAIN_LOG_FILE_MAX_SIZE} == +([[:digit:]]) && MAIN_LOG_FILE_MAX_SIZE -gt DD_BLOCK_SIZE ]] || {
		echo "MAIN_LOG_FILE_MAX_SIZE is not valid: ${MAIN_LOG_FILE_MAX_SIZE}"
		return 1
	}

	[[ ${MAIN_LOG_FILE_ALLOWANCE} == +([[:digit:]]) && MAIN_LOG_FILE_ALLOWANCE -gt DD_BLOCK_SIZE && MAIN_LOG_FILE_ALLOWANCE -lt MAIN_LOG_FILE_MAX_SIZE ]] || {
		echo "MAIN_LOG_FILE_ALLOWANCE is not valid: ${MAIN_LOG_FILE_ALLOWANCE}"
		return 1
	}

	for S in SIGQUIT SIGINT SIGKILL SIGTERM; do
		eval "
			function catch_${S} { signal_caught_callback ${S}; }
			trap catch_${S} ${S}
		"
	done

	mkdir -p "${LOG_DIR}"
	start_tcpdump_loop

	for (( I = 1;; I = (I + 1) % 10000 )); do
		read -t 1

		[[ ${QUIT} == true ]] && break

		if (( (I % TCPDUMP_CHECK_INTERVALS) == 0 )); then
			NEW_DATE=$(date +%F)

			if [[ ${NEW_DATE} != "${CURRENT_DATE}" ]]; then
				log "A new day has come."

				if read -rd '' FILE; then
					log "Deleting ${OLD}-days old files."

					while
						log "Deleting ${FILE}."
						rm -f -- "${FILE}"
						read -r FILE
					do
						continue
					done

					log "Done."
				fi < <(exec find "${LOG_DIR}" -name "${CAPTURE_FILE_PATTERN}" -daystart -ctime "+${OLD}" -print0)  ## Or -mtime?

				restart_tcpdump
			fi
		fi

		if (( (I % MAIN_LOG_CHECK_INTERVALS) == 0 )); then
			SIZE=$(stat --printf=%s "${MAIN_LOG_FILE}")

			if [[ ${SIZE} == +([[:digit:]]) && SIZE -gt MAIN_LOG_FILE_MAX_SIZE ]]; then
				log "Reducing log data in ${MAIN_LOG_FILE}."
				TEMP_FILE=${TEMP_DIR}/tcpdump-${RANDOM}.tmp
				SKIP=$(( (SIZE - (MAIN_LOG_FILE_MAX_SIZE - MAIN_LOG_FILE_ALLOWANCE)) / DD_BLOCK_SIZE ))

				if
					dd "bs=${DD_BLOCK_SIZE}" "skip=${SKIP}" "if=${MAIN_LOG_FILE}" "of=${TEMP_FILE}" \
					&& cat "${TEMP_FILE}" > "${MAIN_LOG_FILE}" \
					&& rm -f "${TEMP_FILE}"
				then
					log "Done."
				else
					log "Failed.  Something went wrong."
				fi
			fi
		fi
	done

	log "Shutting down."
	check_tcpdump && stop_tcpdump
	log "----------------------------------------"
}

# Start.

main
Please check for updates in https://github.com/konsolebox/script...mp-master.bash.
Posted in Howtos
Views 9369 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 05:49 AM.

Main Menu
Advertisement
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration