Your IP : 3.15.148.168


Current Path : /usr/bin/
Upload File :
Current File : //usr/bin/varnishreload

#!/bin/sh
#
# Copyright (c) 2006-2021 Varnish Software AS
# All rights reserved.
#
# Author: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
# Author: Javier Bartley <javierbartley@yahoo.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

set -e
set -u

MAXIMUM=
VCL_FILE=
VCL_LABEL=
VCL_NAME=
WARMUP=
WORK_DIR=

SCRIPT=$0

usage() {
	test $# -eq 1 &&
	printf 'Error: %s.\n\n' "$1"

	cat <<-EOF
	Usage: $SCRIPT [-l <label>] [-m <max>] [-n <workdir>]
	           [-p <prefix>] [-w <warmup>] [<file>]
	       $SCRIPT -h

	Reload and use a VCL on a running Varnish instance.

	Available options:
	-h           : show this help and exit
	-l <label>   : name of the VCL label to reload
	-m <max>     : maximum number of available reloads to leave behind
	-n <workdir> : specify the name or directory for the varnishd instance
	-p <prefix>  : prefix for the name of the loaded and discarded VCLs
	-w <warmup>  : the number of seconds between load and use operations

	When <label> is empty or missing, the active VCL is reloaded. If the
	reloaded VCL is a label, the underlying VCL program is reloaded, and
	the label is updated to reference the new program.

	When <prefix> is empty or missing, the names of the loaded and later
	discarded VCLs will start with 'reload'.

	When <file> is empty or missing, the selected VCL's file is used but
	reload will fail if it wasn't loaded from a file. The <file>, when
	specified, is passed as-is to the vcl.load command. Refer to the
	varnishd manual regarding the handling of absolute or relative paths.

	Upon success, the name of the loaded VCL is constructed from the
	prefix, current date and time, like for example:

	    $(VCL_PREFIX=reload vcl_reload_name)

	Afterwards available VCLs created by this script are discarded until
	<max> are left, unless it was empty or undefined. VCLs referenced by
	a label aren't discarded.
	EOF
	exit $#
}

varnishadm() {
	if ! OUTPUT=$(command varnishadm -n "$WORK_DIR" -- "$@" 2>&1)
	then
		echo "Command: varnishadm -n '$WORK_DIR' -- $*"
		echo
		echo "$OUTPUT"
		echo
		return 1
	fi >&2
	echo "$OUTPUT" 2>/dev/null # silence "broken pipe" diagnostics
}

fail() {
	echo "Error: $*" >&2
	exit 1
}

vcl_reload_name() {
	# NB: The PID compensates the lack of sub-second resolution.
	# Varnish will vcl.list in chronological vcl.load order anyway.
	printf '%s_%s_%s' "$VCL_PREFIX" "$(date -u +%Y%m%d_%H%M%S)" $$
}

awk_vcl_list() {
	printf '%s' "$VCL_LIST" | awk -F ' *|/' "$@"
}

active_vcl() {
	awk_vcl_list '$1 == "active" {print $5}'
}

find_label_ref() {
	awk_vcl_list '$5 == "'"$VCL_LABEL"'" && $6 == "->" {print $7}'
}

find_vcl_file() {
	varnishadm vcl.show -v "$VCL_NAME" |
	awk '$1 == "//" && $2 == "VCL.SHOW" {print; exit}' | {
		# all this ceremony to handle blanks in FILE
		read -r DELIM VCL_SHOW INDEX SIZE FILE &&
		echo "$FILE"
	} ||
	fail "failed to get the VCL file name"
}

find_vcl_reloads() {
	awk_vcl_list -v regex="^${VCL_PREFIX}_[0-9]+_[0-9]+_[0-9]+$"  'NF == 5 &&
		$1 == "available" &&
		$5 ~ regex {print $5}'
}

while getopts hl:m:n:p:w: OPT
do
	case $OPT in
	h) usage ;;
	l) VCL_LABEL=$OPTARG ;;
	m) MAXIMUM=$OPTARG ;;
	n) WORK_DIR=$OPTARG ;;
	p) VCL_PREFIX=$OPTARG ;;
	w) WARMUP=$OPTARG ;;
	*) usage "wrong usage" >&2 ;;
	esac
done

shift $((OPTIND - 1))

test $# -gt 1 && usage "too many arguments" >&2
test $# -eq 1 && VCL_FILE=$1

VCL_LIST=$(varnishadm vcl.list) ||
fail "failed to get the VCL list"

VCL_PREFIX=${VCL_PREFIX:-reload}

VCL_PREFIX_CHK="$(printf '%s' "$VCL_PREFIX" | tr '[:space:]' :)"

test "$VCL_PREFIX" = "$VCL_PREFIX_CHK" ||
fail "illegal character in VCL prefix ('\\n')"

VCL_PREFIX_CHK="$(printf '%s' "$VCL_PREFIX" | tr -d A-Za-z0-9-_)"

if [ -n "$VCL_PREFIX_CHK" ]
then
	VCL_PREFIX_CHK="$(printf '%c' "$VCL_PREFIX_CHK")"
	fail "illegal character in VCL prefix ('$VCL_PREFIX_CHK')"
fi

VCL_PREFIX_CHK=$(printf '%c' "$VCL_PREFIX")

test "$VCL_PREFIX_CHK" != _ ||
fail "illegal leading character in VCL prefix ('_')"

if [ -z "$VCL_LABEL" ]
then
	VCL_NAME=$(active_vcl)
	VCL_LABEL=$VCL_NAME
	VCL_REF=$(find_label_ref)
	if [ -n "$VCL_REF" ]
	then
		# active VCL is a label, swap
		VCL_LABEL=$VCL_NAME
		VCL_NAME=$VCL_REF
	else
		# not a label after all
		VCL_LABEL=
	fi
else
	VCL_NAME=$(find_label_ref)
fi

test -n "$VCL_NAME" ||
fail "'$VCL_LABEL' is not a label"

if [ -z "$VCL_FILE" ]
then
	VCL_FILE=$(find_vcl_file)

	case $VCL_FILE in
	/*) ;;
	*) fail "VCL file not found for $VCL_NAME (got $VCL_FILE)" ;;
	esac
fi

RELOAD_NAME=$(vcl_reload_name)

varnishadm vcl.load "$RELOAD_NAME" "\"$VCL_FILE\"" >/dev/null
echo "VCL '$RELOAD_NAME' compiled"

test -n "$WARMUP" && sleep "$WARMUP"

if [ -n "$VCL_LABEL" ]
then
	varnishadm vcl.label "$VCL_LABEL" "$RELOAD_NAME" >/dev/null
	echo "VCL label '$VCL_LABEL' references '$RELOAD_NAME'"
fi

if [ "$VCL_NAME" = "$(active_vcl)" ]
then
	varnishadm vcl.use "$RELOAD_NAME"
fi

test -n "$MAXIMUM" || exit 0
test "$MAXIMUM" -ge 0 || exit 0

VCL_LIST=$(varnishadm vcl.list) ||
fail "failed to refresh the VCL list"

AVAILABLE=$(find_vcl_reloads | wc -l)
DISCARDED=$((AVAILABLE - MAXIMUM))

test "$DISCARDED" -gt 0 || exit 0

find_vcl_reloads |
head -n "$DISCARDED" |
while read -r DISCARD_NAME
do
	varnishadm vcl.discard "$DISCARD_NAME" >/dev/null
	echo "VCL '$DISCARD_NAME' discarded"
done