diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
commit | 45d6379135504814ab723b57f0eb8be23393a51d (patch) | |
tree | d4f2ec4acca824a8446387a758b0ce4238a4dffa /bin/tests/system/conf.sh.common | |
parent | Initial commit. (diff) | |
download | bind9-45d6379135504814ab723b57f0eb8be23393a51d.tar.xz bind9-45d6379135504814ab723b57f0eb8be23393a51d.zip |
Adding upstream version 1:9.16.44.upstream/1%9.16.44upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | bin/tests/system/conf.sh.common | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common new file mode 100644 index 0000000..e87acca --- /dev/null +++ b/bin/tests/system/conf.sh.common @@ -0,0 +1,744 @@ +#!/bin/sh +# +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +testsock6() { + if test -n "$PERL" && $PERL -e "use IO::Socket::INET6;" 2> /dev/null + then + $PERL "$TOP/bin/tests/system/testsock6.pl" "$@" + else + false + fi +} + +export LANG=C + +. ${TOP}/version + +# +# Common lists of system tests to run. +# +# The following tests are hard-coded to use ports 5300 and 9953. For +# this reason, these must be run sequentially. +# +# Sequential tests that only run on unix/linux should be added to +# SEQUENTIAL_UNIX in conf.sh.in; those that only run on windows should +# be added to SEQUENTIAL_WINDOWS in conf.sh.win32. +# +SEQUENTIAL_COMMON="ecdsa eddsa tkey" + +# +# These tests can use ports assigned by the caller (other than 5300 +# and 9953). Because separate blocks of ports can be used for teach +# test, these tests can be run in parallel. +# +# Parallel tests that only run on unix/linux should be added to +# PARALLEL_UNIX in conf.sh.in; those that only run on windows should +# be added to PARALLEL_WINDOWS in conf.sh.win32. +# +# Note: some of the longer-running tests such as serve-stale and +# rpzrecurse are scheduled first, in order to get more benefit from +# parallelism. +# +PARALLEL_COMMON="dnssec rpzrecurse serve-stale dupsigs \ +acl \ +additional \ +addzone \ +allow-query \ +auth \ +autosign \ +builtin \ +cacheclean \ +case \ +catz \ +cds \ +chain \ +checkconf \ +checkds \ +checknames \ +checkzone \ +database \ +digdelv \ +dlz \ +dlzexternal \ +dns64 \ +dscp \ +dsdigest \ +dyndb \ +ednscompliance \ +emptyzones \ +fetchlimit \ +filter-aaaa \ +formerr \ +forward \ +geoip2 \ +glue \ +idna \ +inline \ +integrity \ +ixfr \ +journal \ +kasp \ +keepalive \ +keymgr2kasp \ +legacy \ +limits \ +masterfile \ +masterformat \ +metadata \ +mirror \ +mkeys \ +names \ +notify \ +nsec3 \ +nslookup \ +nsupdate \ +nzd2nzf \ +padding \ +pending \ +pipelined \ +qmin \ +reclimit \ +redirect \ +resolver \ +rndc \ +rootkeysentinel \ +rpz \ +rrchecker \ +rrl \ +rrsetorder \ +rsabigexponent \ +runtime \ +sfcache \ +shutdown \ +smartsign \ +sortlist \ +spf \ +staticstub \ +statistics \ +statschannel \ +stress \ +stub \ +synthfromdnssec \ +timeouts \ +tcp \ +tools \ +tsig \ +tsiggss \ +ttl \ +unknown \ +upforwd \ +verify \ +views \ +wildcard \ +xfer \ +xferquota \ +zero \ +zonechecks" + +# +# Set up color-coded test output +# +if [ ${SYSTEMTEST_FORCE_COLOR:-0} -eq 1 ] || test -t 1 && type tput > /dev/null 2>&1 && tput setaf 7 > /dev/null 2>&1 ; then + export COLOR_END=`tput setaf 4` # blue + export COLOR_FAIL=`tput setaf 1` # red + export COLOR_INFO=`tput bold` # bold + export COLOR_NONE=`tput sgr0` + export COLOR_PASS=`tput setaf 2` # green + export COLOR_START=`tput setaf 4` # blue + export COLOR_WARN=`tput setaf 3` # yellow +else + # set to empty strings so printf succeeds + export COLOR_END='' + export COLOR_FAIL='' + export COLOR_INFO='' + export COLOR_NONE='' + export COLOR_PASS='' + export COLOR_START='' + export COLOR_WARN='' +fi + +export SYSTESTDIR="`basename $PWD`" + +if type printf > /dev/null 2>&1 +then + echofail () { + printf "${COLOR_FAIL}%s${COLOR_NONE}\n" "$*" + } + echowarn () { + printf "${COLOR_WARN}%s${COLOR_NONE}\n" "$*" + } + echopass () { + printf "${COLOR_PASS}%s${COLOR_NONE}\n" "$*" + } + echoinfo () { + printf "${COLOR_INFO}%s${COLOR_NONE}\n" "$*" + } + echostart () { + printf "${COLOR_START}%s${COLOR_NONE}\n" "$*" + } + echoend () { + printf "${COLOR_END}%s${COLOR_NONE}\n" "$*" + } + echo_i() { + printf '%s\n' "$*" | while IFS= read -r __LINE ; do + echoinfo "I:$SYSTESTDIR:$__LINE" + done + } + + echo_ic() { + printf '%s\n' "$*" | while IFS= read -r __LINE ; do + echoinfo "I:$SYSTESTDIR: $__LINE" + done + } + + echo_d() { + printf '%s\n' "$*" | while IFS= read -r __LINE ; do + echoinfo "D:$SYSTESTDIR:$__LINE" + done + } +else + echofail () { + echo "$*" + } + echowarn () { + echo "$*" + } + echopass () { + echo "$*" + } + echoinfo () { + echo "$*" + } + echostart () { + echo "$*" + } + echoend () { + echo "$*" + } + + echo_i() { + echo "$@" | while IFS= read -r __LINE ; do + echoinfo "I:$SYSTESTDIR:$__LINE" + done + } + + echo_ic() { + echo "$@" | while IFS= read -r __LINE ; do + echoinfo "I:$SYSTESTDIR: $__LINE" + done + } + + echo_d() { + echo "$@" | while IFS= read -r __LINE ; do + echoinfo "D:$SYSTESTDIR:$__LINE" + done + } +fi + +cat_i() { + while IFS= read -r __LINE ; do + echoinfo "I:$SYSTESTDIR:$__LINE" + done +} + +cat_d() { + while IFS= read -r __LINE ; do + echoinfo "D:$SYSTESTDIR:$__LINE" + done +} + +digcomp() { + output=`$PERL $SYSTEMTESTTOP/digcomp.pl "$@"` + result=$? + [ -n "$output" ] && { echo "digcomp failed:"; echo "$output"; } | cat_i + return $result +} + +start_server() { + $PERL "$TOP_SRCDIR/bin/tests/system/start.pl" "$SYSTESTDIR" "$@" +} + +stop_server() { + $PERL "$TOP_SRCDIR/bin/tests/system/stop.pl" "$SYSTESTDIR" "$@" +} + +send() { + $PERL "$TOP_SRCDIR/bin/tests/system/send.pl" "$@" +} + +# +# Useful variables in test scripts +# + +# The following script sets the following algorithm-related variables. These +# are selected randomly at runtime from a list of supported algorithms. The +# randomization is deterministic and remains stable for a period of time for a +# given platform. +# +# Default algorithm for testing. +# DEFAULT_ALGORITHM +# DEFAULT_ALGORITHM_NUMBER +# DEFAULT_BITS +# +# This is an alternative algorithm for test cases that require more than one +# algorithm (for example algorithm rollover). Must be different from +# DEFAULT_ALGORITHM. +# ALTERNATIVE_ALGORITHM +# ALTERNATIVE_ALGORITHM_NUMBER +# ALTERNATIVE_BITS +# +# This is an algorithm that is used for tests against the "disable-algorithms" +# configuration option. Must be different from above algorithms. +# DISABLED_ALGORITHM +# DISABLED_ALGORITHM_NUMBER +# DISABLED_BITS +# +# There are multiple algoritms sets to choose from (see get_algorithms.py). To +# override the default choice, set the ALGORITHM_SET env var (see mkeys system +# test for example). +if test -x "$PYTHON" && test -x "$KEYGEN"; then + eval "$($PYTHON "$TOP_SRCDIR/bin/tests/system/get_algorithms.py")" +else + # 9.16 workarounds + # - for ./configure which calls bin/tests/system/cleanall.sh, which + # includes this file before $KEYGEN is compiled + # - for our Windows CI which lacks Python + DEFAULT_ALGORITHM=ECDSAP256SHA256 + DEFAULT_ALGORITHM_NUMBER=13 + DEFAULT_BITS=256 + ALTERNATIVE_ALGORITHM=RSASHA256 + ALTERNATIVE_ALGORITHM_NUMBER=8 + ALTERNATIVE_BITS=1280 + DISABLED_ALGORITHM=ECDSAP384SHA384 + DISABLED_ALGORITHM_NUMBER=14 + DISABLED_BITS=384 +fi + +# Default HMAC algorithm. +export DEFAULT_HMAC=hmac-sha256 + +# +# Useful functions in test scripts +# + +# assert_int_equal: compare two integer variables, $1 and $2 +# +# If $1 and $2 are equal, return 0; if $1 and $2 are not equal, report +# the error using the description of the tested variable provided in $3 +# and return 1. +assert_int_equal() { + found="$1" + expected="$2" + description="$3" + + if [ "${expected}" -ne "${found}" ]; then + echo_i "incorrect ${description}: got ${found}, expected ${expected}" + return 1 + fi + + return 0 +} + +# keyfile_to_keys_section: helper function for keyfile_to_*_keys() which +# converts keyfile data into a key-style trust anchor configuration +# section using the supplied parameters +keyfile_to_keys() { + section_name=$1 + key_prefix=$2 + shift + shift + echo "$section_name {" + for keyname in $*; do + awk '!/^; /{ + printf "\t\""$1"\" " + printf "'"$key_prefix "'" + printf $4 " " $5 " " $6 " \"" + for (i=7; i<=NF; i++) printf $i + printf "\";\n" + }' $keyname.key + done + echo "};" +} + +# keyfile_to_dskeys_section: helper function for keyfile_to_*_dskeys() +# converts keyfile data into a DS-style trust anchor configuration +# section using the supplied parameters +keyfile_to_dskeys() { + section_name=$1 + key_prefix=$2 + shift + shift + echo "$section_name {" + for keyname in $*; do + $DSFROMKEY $keyname.key | \ + awk '!/^; /{ + printf "\t\""$1"\" " + printf "'"$key_prefix "'" + printf $4 " " $5 " " $6 " \"" + for (i=7; i<=NF; i++) printf $i + printf "\";\n" + }' + done + echo "};" +} + +# keyfile_to_trusted_keys: convert key data contained in the keyfile(s) +# provided to a "trust-keys" section suitable for including in a +# resolver's configuration file +keyfile_to_trusted_keys() { + keyfile_to_keys "trusted-keys" "" $* +} + +# keyfile_to_static_keys: convert key data contained in the keyfile(s) +# provided to a *static-key* "trust-anchors" section suitable for including in +# a resolver's configuration file +keyfile_to_static_keys() { + keyfile_to_keys "trust-anchors" "static-key" $* +} + +# keyfile_to_initial_keys: convert key data contained in the keyfile(s) +# provided to an *initial-key* "trust-anchors" section suitable for including +# in a resolver's configuration file +keyfile_to_initial_keys() { + keyfile_to_keys "trust-anchors" "initial-key" $* +} + +# keyfile_to_static_ds_keys: convert key data contained in the keyfile(s) +# provided to a *static-ds* "trust-anchors" section suitable for including in a +# resolver's configuration file +keyfile_to_static_ds() { + keyfile_to_dskeys "trust-anchors" "static-ds" $* +} + +# keyfile_to_initial_ds_keys: convert key data contained in the keyfile(s) +# provided to an *initial-ds* "trust-anchors" section suitable for including +# in a resolver's configuration file +keyfile_to_initial_ds() { + keyfile_to_dskeys "trust-anchors" "initial-ds" $* +} + +# keyfile_to_key_id: convert a key file name to a key ID +# +# For a given key file name (e.g. "Kexample.+013+06160") provided as $1, +# print the key ID with leading zeros stripped ("6160" for the +# aforementioned example). +keyfile_to_key_id() { + echo "$1" | sed "s/.*+0\{0,4\}//" +} + +# private_type_record: write a private type record recording the state of the +# signing process +# +# For a given zone ($1), algorithm number ($2) and key file ($3), print the +# private type record with default type value of 65534, indicating that the +# signing process for this key is completed. +private_type_record() { + _zone=$1 + _algorithm=$2 + _keyfile=$3 + + _id=$(keyfile_to_key_id "$_keyfile") + + printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" +} + +# nextpart*() - functions for reading files incrementally +# +# These functions aim to facilitate looking for (or waiting for) +# messages which may be logged more than once throughout the lifetime of +# a given named instance by outputting just the part of the file which +# has been appended since the last time we read it. +# +# Calling some of these functions causes temporary *.prev files to be +# created that need to be cleaned up manually (usually by a given system +# test's clean.sh script). +# +# Note that unlike other nextpart*() functions, nextpartread() is not +# meant to be directly used in system tests; its sole purpose is to +# reduce code duplication below. +# +# A quick usage example: +# +# $ echo line1 > named.log +# $ echo line2 >> named.log +# $ nextpart named.log +# line1 +# line2 +# $ echo line3 >> named.log +# $ nextpart named.log +# line3 +# $ nextpart named.log +# $ echo line4 >> named.log +# $ nextpartpeek named.log +# line4 +# $ nextpartpeek named.log +# line4 +# $ nextpartreset named.log +# $ nextpartpeek named.log +# line1 +# line2 +# line3 +# line4 +# $ nextpart named.log +# line1 +# line2 +# line3 +# line4 +# $ nextpart named.log +# $ + +# nextpartreset: reset the marker used by nextpart() and nextpartpeek() +# so that it points to the start of the given file +nextpartreset() { + echo "0" > $1.prev +} + +# nextpartread: read everything that's been appended to a file since the +# last time nextpart() was called and print it to stdout, print the +# total number of lines read from that file so far to file descriptor 3 +nextpartread() { + [ -f $1.prev ] || nextpartreset $1 + prev=`cat $1.prev` + awk "NR > $prev "'{ print } + END { print NR > "/dev/stderr" }' $1 2>&3 +} + +# nextpart: read everything that's been appended to a file since the +# last time nextpart() was called +nextpart() { + nextpartread $1 3> $1.prev.tmp + mv $1.prev.tmp $1.prev +} + +# nextpartpeek: read everything that's been appended to a file since the +# last time nextpart() was called +nextpartpeek() { + nextpartread $1 3> /dev/null +} + +# _search_log: look for message $1 in file $2 with nextpart(). +_search_log() ( + msg="$1" + file="$2" + nextpart "$file" | grep -F -e "$msg" > /dev/null +) + +# _search_log_peek: look for message $1 in file $2 with nextpartpeek(). +_search_log_peek() ( + msg="$1" + file="$2" + nextpartpeek "$file" | grep -F -e "$msg" > /dev/null +) + +# wait_for_log: wait until message $2 in file $3 appears. Bail out after +# $1 seconds. This needs to be used in conjunction with a prior call to +# nextpart() or nextpartreset() on the same file to guarantee the offset is +# set correctly. Tests using wait_for_log() are responsible for cleaning up +# the created <file>.prev files. +wait_for_log() ( + timeout="$1" + msg="$2" + file="$3" + retry_quiet "$timeout" _search_log "$msg" "$file" && return 0 + echo_i "exceeded time limit waiting for '$msg' in $file" + return 1 +) + +# wait_for_log_peek: similar to wait_for_log() but peeking, so the file offset +# does not change. +wait_for_log_peek() ( + timeout="$1" + msg="$2" + file="$3" + retry_quiet "$timeout" _search_log_peek "$msg" "$file" && return 0 + echo_i "exceeded time limit waiting for '$msg' in $file" + return 1 +) + +# _retry: keep running a command until it succeeds, up to $1 times, with +# one-second intervals, optionally printing a message upon every attempt +_retry() { + __retries="${1}" + shift + + while :; do + if "$@"; then + return 0 + fi + __retries=$((__retries-1)) + if [ "${__retries}" -gt 0 ]; then + if [ "${__retry_quiet}" -ne 1 ]; then + echo_i "retrying" + fi + sleep 1 + else + return 1 + fi + done +} + +# retry: call _retry() in verbose mode +retry() { + __retry_quiet=0 + _retry "$@" +} + +# retry_quiet: call _retry() in silent mode +retry_quiet() { + __retry_quiet=1 + _retry "$@" +} + +# _repeat: keep running command up to $1 times, unless it fails +_repeat() ( + __retries="${1}" + shift + while :; do + if ! "$@"; then + return 1 + fi + __retries=$((__retries-1)) + if [ "${__retries}" -le 0 ]; then + break + fi + done + return 0 +) + +rndc_reload() { + $RNDC -c ../common/rndc.conf -s $2 -p ${CONTROLPORT} reload $3 2>&1 | sed 's/^/'"I:$SYSTESTDIR:$1"' /' + # reloading single zone is synchronous, if we're reloading whole server + # we need to wait for reload to finish + if [ -z "$3" ]; then + for __try in 0 1 2 3 4 5 6 7 8 9; do + $RNDC -c ../common/rndc.conf -s $2 -p ${CONTROLPORT} status | grep "reload/reconfig in progress" > /dev/null || break + sleep 1 + done + fi +} + +rndc_reconfig() { + $RNDC -c ../common/rndc.conf -s $2 -p ${CONTROLPORT} reconfig 2>&1 | sed 's/^/'"I:$SYSTESTDIR:$1"' /' + for __try in 0 1 2 3 4 5 6 7 8 9; do + $RNDC -c ../common/rndc.conf -s $2 -p ${CONTROLPORT} status | grep "reload/reconfig in progress" > /dev/null || break + sleep 1 + done +} + +# rndc_dumpdb: call "rndc dumpdb [...]" and wait until it completes +# +# The first argument is the name server instance to send the command to, in the +# form of "nsX" (where "X" is the instance number), e.g. "ns5". The remaining +# arguments, if any, are appended to the rndc command line after "dumpdb". +# +# Control channel configuration for the name server instance to send the +# command to must match the contents of bin/tests/system/common/rndc.conf. +# +# rndc output is stored in a file called rndc.out.test${n}; the "n" variable is +# required to be set by the calling tests.sh script. +# +# Return 0 if the dump completes successfully; return 1 if rndc returns an exit +# code other than 0 or if the "; Dump complete" string does not appear in the +# dump within 10 seconds. +rndc_dumpdb() { + __ret=0 + __dump_complete=0 + __server="${1}" + __ip="10.53.0.$(echo "${__server}" | tr -c -d "0-9")" + + shift + ${RNDC} -c ../common/rndc.conf -p "${CONTROLPORT}" -s "${__ip}" dumpdb "$@" > "rndc.out.test${n}" 2>&1 || __ret=1 + + for _ in 0 1 2 3 4 5 6 7 8 9 + do + if grep '^; Dump complete$' "${__server}/named_dump.db" > /dev/null; then + mv "${__server}/named_dump.db" "${__server}/named_dump.db.test${n}" + __dump_complete=1 + break + fi + sleep 1 + done + + if [ ${__dump_complete} -eq 0 ]; then + echo_i "timed out waiting for 'rndc dumpdb' to finish" + __ret=1 + fi + + return ${__ret} +} + +# get_dig_xfer_stats: extract transfer statistics from dig output stored +# in $1, converting them to a format used by some system tests. +get_dig_xfer_stats() { + LOGFILE="$1" + sed -n "s/^;; XFR size: .*messages \([0-9][0-9]*\).*/messages=\1/p" "${LOGFILE}" + sed -n "s/^;; XFR size: \([0-9][0-9]*\) records.*/records=\1/p" "${LOGFILE}" + sed -n "s/^;; XFR size: .*bytes \([0-9][0-9]*\).*/bytes=\1/p" "${LOGFILE}" +} + +# get_named_xfer_stats: from named log file $1, extract transfer +# statistics for the last transfer for peer $2 and zone $3 (from a log +# message which has to contain the string provided in $4), converting +# them to a format used by some system tests. +get_named_xfer_stats() { + LOGFILE="$1" + PEER="`echo $2 | sed 's/\./\\\\./g'`" + ZONE="`echo $3 | sed 's/\./\\\\./g'`" + MESSAGE="$4" + grep " ${PEER}#.*${MESSAGE}:" "${LOGFILE}" | \ + sed -n "s/.* '${ZONE}\/.* \([0-9][0-9]*\) messages.*/messages=\1/p" | tail -1 + grep " ${PEER}#.*${MESSAGE}:" "${LOGFILE}" | \ + sed -n "s/.* '${ZONE}\/.* \([0-9][0-9]*\) records.*/records=\1/p" | tail -1 + grep " ${PEER}#.*${MESSAGE}:" "${LOGFILE}" | \ + sed -n "s/.* '${ZONE}\/.* \([0-9][0-9]*\) bytes.*/bytes=\1/p" | tail -1 +} + +# copy_setports - Copy Configuration File and Replace Ports +# +# Convenience function to copy a configuration file, replacing the tokens +# QUERYPORT, CONTROLPORT and EXTRAPORT[1-8] with the values of the equivalent +# environment variables. (These values are set by "run.sh", which calls the +# scripts invoking this function.) +# +# Usage: +# copy_setports infile outfile +# +copy_setports() { + # The indirect method of handling the substitution of the PORT variables + # (defining "atsign" then substituting for it in the "sed" statement) is + # required to prevent the "Configure" script (in the win32utils/ directory) + # from replacing the <at>PORT<at> substitution tokens when it processes + # this file and produces conf.sh. + atsign="@" + sed -e "s/${atsign}PORT${atsign}/${PORT}/g" \ + -e "s/${atsign}EXTRAPORT1${atsign}/${EXTRAPORT1}/g" \ + -e "s/${atsign}EXTRAPORT2${atsign}/${EXTRAPORT2}/g" \ + -e "s/${atsign}EXTRAPORT3${atsign}/${EXTRAPORT3}/g" \ + -e "s/${atsign}EXTRAPORT4${atsign}/${EXTRAPORT4}/g" \ + -e "s/${atsign}EXTRAPORT5${atsign}/${EXTRAPORT5}/g" \ + -e "s/${atsign}EXTRAPORT6${atsign}/${EXTRAPORT6}/g" \ + -e "s/${atsign}EXTRAPORT7${atsign}/${EXTRAPORT7}/g" \ + -e "s/${atsign}EXTRAPORT8${atsign}/${EXTRAPORT8}/g" \ + -e "s/${atsign}CONTROLPORT${atsign}/${CONTROLPORT}/g" \ + -e "s/${atsign}DEFAULT_ALGORITHM${atsign}/${DEFAULT_ALGORITHM}/g" \ + -e "s/${atsign}DEFAULT_ALGORITHM_NUMBER${atsign}/${DEFAULT_ALGORITHM_NUMBER}/g" \ + -e "s/${atsign}DEFAULT_BITS${atsign}/${DEFAULT_BITS}/g" \ + -e "s/${atsign}ALTERNATIVE_ALGORITHM${atsign}/${ALTERNATIVE_ALGORITHM}/g" \ + -e "s/${atsign}ALTERNATIVE_ALGORITHM_NUMBER${atsign}/${ALTERNATIVE_ALGORITHM_NUMBER}/g" \ + -e "s/${atsign}ALTERNATIVE_BITS${atsign}/${ALTERNATIVE_BITS}/g" \ + -e "s/${atsign}DEFAULT_HMAC${atsign}/${DEFAULT_HMAC}/g" \ + -e "s/${atsign}DISABLED_ALGORITHM${atsign}/${DISABLED_ALGORITHM}/g" \ + -e "s/${atsign}DISABLED_ALGORITHM_NUMBER${atsign}/${DISABLED_ALGORITHM_NUMBER}/g" \ + -e "s/${atsign}DISABLED_BITS${atsign}/${DISABLED_BITS}/g" \ + $1 > $2 +} |