summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:55:53 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:55:53 +0000
commit3d0386f27ca66379acf50199e1d1298386eeeeb8 (patch)
treef87bd4a126b3a843858eb447e8fd5893c3ee3882 /scripts
parentInitial commit. (diff)
downloadknot-resolver-3d0386f27ca66379acf50199e1d1298386eeeeb8.tar.xz
knot-resolver-3d0386f27ca66379acf50199e1d1298386eeeeb8.zip
Adding upstream version 3.2.1.upstream/3.2.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rwxr-xr-xscripts/build-in-obs.sh29
-rwxr-xr-xscripts/coverage_c_combine.sh24
-rwxr-xr-xscripts/coverage_env.sh40
-rwxr-xr-xscripts/embed-lua.sh10
-rwxr-xr-xscripts/gen-cdefs.sh72
-rwxr-xr-xscripts/kresd-host.lua114
-rwxr-xr-xscripts/kresd-query.lua64
-rw-r--r--scripts/kresd.apparmor29
-rwxr-xr-xscripts/launch-test-instance.sh26
-rwxr-xr-xscripts/luacov_gen_empty.sh17
-rwxr-xr-xscripts/luacov_to_info.lua56
-rwxr-xr-xscripts/make-archive.sh15
-rwxr-xr-xscripts/make-distrofiles.sh53
-rwxr-xr-xscripts/make-srpm.sh13
-rwxr-xr-xscripts/map_install_src.lua167
-rwxr-xr-xscripts/obs-testbuild.sh38
16 files changed, 767 insertions, 0 deletions
diff --git a/scripts/build-in-obs.sh b/scripts/build-in-obs.sh
new file mode 100755
index 0000000..7aea0d3
--- /dev/null
+++ b/scripts/build-in-obs.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+# Example usage:
+# 1. place tarball to be released in git root dir
+# 2. scripts/make-distrofiles.sh -s
+# 3. scripts/build-in-obs.sh knot-resolver-latest
+
+project=home:CZ-NIC:$1
+package=knot-resolver
+
+if ! [[ "$1" == *-devel || "$1" == *-testing ]]; then
+ read -p "Pushing to '$project', are you sure? [y/N]: " yn
+ case $yn in
+ [Yy]* )
+ ;;
+ * )
+ exit 1
+ esac
+fi
+
+osc co "${project}" "${package}"
+pushd "${project}/${package}"
+osc del * ||:
+cp -L ../../*.orig.tar.xz ../../*.debian.tar.xz ../../*.dsc ./
+cp -rL ../../distro/rpm/* ./
+cp -rL ../../distro/arch/* ./
+osc addremove
+osc ci -n
+popd
diff --git a/scripts/coverage_c_combine.sh b/scripts/coverage_c_combine.sh
new file mode 100755
index 0000000..23006c1
--- /dev/null
+++ b/scripts/coverage_c_combine.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# $1 = top source directory
+# $2 = coverage data directory path
+# $3 = output directory for *.info files
+
+set -o errexit -o nounset
+shopt -s nullglob
+IFS=$'\n'
+
+TOPSRCDIR="$1"
+DATAROOT="$2"
+OUTDIR="$3"
+
+cd "${TOPSRCDIR}"
+for COVNAME in $(find "${DATAROOT}" -name .topdir_kresd_coverage)
+do
+ find "${DATAROOT}" -name '*.gcda' -not -path "${DATAROOT}/*" -delete
+ COVDIR="$(dirname "${COVNAME}")"
+ COVDATA_FILENAMES=("${COVDIR}"/*) # filenames in BASH array
+ (( ${#COVDATA_FILENAMES[*]} )) || continue # skip empty dirs
+
+ cp -r -t ${TOPSRCDIR} "${COVDIR}"/*
+ ${LCOV} -q --no-external --capture -d lib -d daemon -d modules -o "$(mktemp -p "${OUTDIR}" -t XXXXXXXX.c.info)" > /dev/null
+done
diff --git a/scripts/coverage_env.sh b/scripts/coverage_env.sh
new file mode 100755
index 0000000..bb85649
--- /dev/null
+++ b/scripts/coverage_env.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# generate variables for coverage testing
+# $1 = top source directory
+# $2 = coverage data directory path
+# $3 = name of test/new subdirectory name
+# $4 = [optional] --export to generate export commands
+
+set -o errexit -o nounset
+shopt -s nullglob
+
+test -z "${COVERAGE:-}" && exit 0 # not enabled, do nothing
+test ! -z "${V:-}" && set -o xtrace # verbose mode
+
+EXPORT=""
+test "${4:-}" == "--export" && EXPORT="export "
+TOPSRCDIR="$1"
+DATAROOT="$2"
+OUTPATH="$2/$3"
+
+# check that output directory is empty
+# beware: Makefile will always call coverage_env.sh for all targets
+# so directories get created but not populated
+# i.e. test -d is not sufficient check
+OUTPATH_FILENAMES=("${OUTPATH}"/*) # filenames in BASH array
+(( ${#OUTPATH_FILENAMES[*]} )) && echo "false" && >&2 echo "fatal: output directory ${OUTPATH} must be empty (or non-existent)" && exit 1
+
+mkdir -p "${OUTPATH}"
+# convert paths to absolute
+pushd "${OUTPATH}" &> /dev/null
+touch .topdir_kresd_coverage
+OUTPATH="$(pwd -P)"
+popd &> /dev/null
+
+# determine GCOV_PREFIX_STRIP value for current source directory
+TOPSRCDIR_SLASHES="${TOPSRCDIR//[^\/]/}" # remove everything except /
+GCOV_PREFIX_STRIP="${#TOPSRCDIR_SLASHES}" # numer of / == number of components
+
+KRESD_COVERAGE_STATS="${OUTPATH}/luacov.stats.out"
+GCOV_PREFIX="${OUTPATH}"
+echo "${EXPORT}KRESD_COVERAGE_STATS=\"${KRESD_COVERAGE_STATS}\" ${EXPORT}GCOV_PREFIX=\"${GCOV_PREFIX}\" ${EXPORT}GCOV_PREFIX_STRIP=\"${GCOV_PREFIX_STRIP}\""
diff --git a/scripts/embed-lua.sh b/scripts/embed-lua.sh
new file mode 100755
index 0000000..80f6cba
--- /dev/null
+++ b/scripts/embed-lua.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+set -e
+# Clean unnecessary stuff from the lua file; note the significant tabulator.
+alias strip="sed -e 's/^[ ]*//g; s/ */ /g; /^--/d; /^$/d'"
+if command -v xxd > /dev/null 2>&1; then
+ strip < "$1" | xxd -i -
+else
+ strip < "$1" | hexdump -v -e '/1 "0x%02X, " " "'
+fi
+exit $?
diff --git a/scripts/gen-cdefs.sh b/scripts/gen-cdefs.sh
new file mode 100755
index 0000000..0627fa4
--- /dev/null
+++ b/scripts/gen-cdefs.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+set -o pipefail -o errexit
+
+if [ "$2" != types ] && [ "$2" != functions ]; then
+ echo "Usage: $0 libkres (types|functions)" >&2
+ echo " and input identifiers, one per line." >&2
+ echo " You need debug symbols in the library." >&2
+ exit 1
+fi
+
+if ! command -v gdb >/dev/null; then
+ echo "Failed to find gdb" >&2
+ exit 1
+fi
+
+if ! command -v sed >/dev/null; then
+ echo "Failed to find GNU sed" >&2
+ exit 1
+fi
+
+if ! sed --version | head -1 | grep -q "GNU sed"; then
+ echo "GNU sed required to run this script" >&2
+fi
+
+# be very precise with the directories for libraries to not pick wrong library
+case "$1" in
+ libknot) library="$(PATH="$(pkg-config libknot --variable=libdir)" command -v "$1.so")" ;;
+ libzscanner) library="$(PATH="$(pkg-config libzscanner --variable=libdir)" command -v "$1.so")" ;;
+ *) library="$(PATH="$(pwd)/lib" command -v "$1.so")"
+esac
+
+if [ -z "$library" ]; then
+ echo "$1 not found. Note: only .so platforms work currently." >&2
+ exit 1
+fi
+
+# Let's use an array to hold command-line arguments, to simplify quoting.
+GDB=(gdb)
+GDB+=(-n -quiet -batch "-symbols=$library")
+GDB+=(-iex "set width unlimited" -iex "set max-value-size unlimited")
+
+grep -v '^#\|^$' | while read -r ident; do
+ if [ "$2" = functions ]; then
+ output="$("${GDB[@]}" --ex "info functions ^$ident\$" \
+ | sed '0,/^All functions/ d; /^File .*:$/ d')"
+ else # types
+ case "$ident" in
+ struct\ *|union\ *|enum\ *)
+ output="$("${GDB[@]}" --ex "ptype $ident" \
+ | sed '0,/^type = /s/^type = /\n/; $ s/$/;/')"
+ ;;
+ *)
+ output="$("${GDB[@]}" --ex "info types ^$ident\$" \
+ | sed -e '0,/^File .*:$/ d' -e '/^File .*:$/,$ d')"
+ # we need to stop early to remove ^^ multiple matches
+ ;;
+ esac
+ fi
+ # LuaJIT FFI blows up on "uint" type
+ output="$(echo "$output" | sed 's/\buint\b/unsigned int/g')"
+ # GDB 8.2+ added source line prefix to output
+ output="$(echo "$output" | sed 's/^[0-9]\+:[[:space:]]*//g')"
+
+ # abort on empty output
+ if [ -z "$(echo "$output" | tr -d "\n;")" ]; then
+ echo "Failed to find cdef of $ident" >&2
+ exit 1
+ fi
+ echo "$output" | grep -v '^$'
+done
+
+exit 0
diff --git a/scripts/kresd-host.lua b/scripts/kresd-host.lua
new file mode 100755
index 0000000..9348716
--- /dev/null
+++ b/scripts/kresd-host.lua
@@ -0,0 +1,114 @@
+#!/usr/bin/env luajit
+-- Work around OS X stripping dyld variables
+cli_bin = 'luajit scripts/kresd-query.lua'
+libdir = os.getenv('DYLD_LIBRARY_PATH')
+if libdir then
+ cli_bin = string.format('DYLD_LIBRARY_PATH="%s" %s', libdir, cli_bin)
+end
+-- Parse CLI arguments
+local function help(rc)
+ print(string.format([[
+Usage: %s [-vdh46D] [-c class] [-t type]
+ [-f keyfile] hostname
+ Queries the DNS for information.
+ The hostname is looked up for IP4, IP6 and mail.
+ Use the -v option to see DNSSEC security information.
+ -t type what type to look for.
+ -c class what class to look for, if not class IN.
+ -C confstr additional kresd-style configuration.
+ -D DNSSEC enable with default root anchor
+ -f keyfile read trust anchors from file, with lines as -y.
+ -v be more verbose, shows nodata and security.
+ -d debug, traces the action, -d -d shows more.
+ -4 use ipv4 network, avoid ipv6.
+ -6 use ipv6 network, avoid ipv4.
+ -h show this usage help.]],
+ arg[0]))
+ return rc
+end
+
+-- Parse CLI arguments
+if #arg < 1 then
+ return help(1)
+end
+local qtypes, qclass, qname = {}, 'IN', nil
+local verbose, config = false, {}
+k = 1 while k <= #arg do
+ local v = arg[k]
+ if v == '-h' or v == '--help' then
+ return help(0)
+ elseif v == '-C' then
+ k = k + 1
+ table.insert(config, arg[k])
+ elseif v == '-D' then
+ table.insert(config, 'trust_anchors.file = "root.keys"')
+ elseif v == '-f' then
+ k = k + 1
+ table.insert(config, string.format('trust_anchors.file = "%s"', arg[k]))
+ elseif v == '-v' then
+ verbose = true
+ elseif v == '-d' then
+ verbose = true
+ table.insert(config, 'verbose(true)')
+ elseif v == '-4' then
+ table.insert(config, 'net.ipv6 = false')
+ elseif v == '-6' then
+ table.insert(config, 'net.ipv4 = false')
+ elseif v == '-c' then
+ k = k + 1
+ qclass = arg[k]:upper()
+ elseif v == '-t' then
+ k = k + 1
+ table.insert(qtypes, arg[k]:upper())
+ elseif v:byte() == string.byte('-') then
+ return help(1)
+ else
+ qname = v
+ -- Check if name is an IP addresses
+ -- @TODO: convert to domain name and make a PTR lookup
+ end
+ k = k + 1
+end
+if not qname then
+ return help(1)
+end
+if #qtypes == 0 then
+ qtypes = {'A', 'AAAA', 'MX'}
+end
+-- Assemble config/query
+for _, qtype in ipairs(qtypes) do
+ query = string.format('-t %s -c %s %s', qtype, qclass, qname)
+ capture = string.format([[
+ local qname = "%s"
+ local qtype = "%s"
+ local qverbose = %s]], qname, qtype, tostring(verbose))..[[
+ local qry = req:resolved()
+ local section = pkt:rrsets(kres.section.ANSWER)
+ for i = 1, #section do
+ local rr = section[i]
+ for k = 1, rr.rrs.count do
+ local rdata = rr:tostring(k - 1)
+ local owner = kres.dname2str(rr:owner())
+ if qverbose then
+ if not qry.flags.DNSSEC_WANT or qry.flags.DNSSEC_INSECURE then
+ rdata = rdata .. " (insecure)"
+ else
+ rdata = rdata .. " (secure)"
+ end
+ end
+ if rr.type == kres.type.A then
+ print(string.format("%s has address %s", owner, rdata))
+ elseif rr.type == kres.type.AAAA then
+ print(string.format("%s has IPv6 address %s", owner, rdata))
+ elseif rr.type == kres.type.MX then
+ print(string.format("%s mail is handled by %s", owner, rdata))
+ elseif rr.type == kres.type.CNAME then
+ print(string.format("%s is an alias for %s", owner, rdata))
+ else
+ print(string.format("%s has %s record %s", owner, qtype, rdata))
+ end
+ end
+ end
+ ]]
+ os.execute(string.format('%s -C \'%s\' %s \'%s\'', cli_bin, table.concat(config, ' '), query, capture))
+end
diff --git a/scripts/kresd-query.lua b/scripts/kresd-query.lua
new file mode 100755
index 0000000..c2d5468
--- /dev/null
+++ b/scripts/kresd-query.lua
@@ -0,0 +1,64 @@
+#!/usr/bin/env luajit
+cli_bin = 'kresd -q -c -'
+-- Work around OS X stripping dyld variables
+libdir = os.getenv('DYLD_LIBRARY_PATH')
+if libdir then
+ cli_bin = string.format('DYLD_LIBRARY_PATH="%s" %s', libdir, cli_bin)
+end
+cli_cmd = [[echo '
+option("ALWAYS_CUT", true)
+%s
+return resolve("%s", kres.type.%s, kres.class.%s, 0,
+function (pkt, req)
+ pkt = kres.pkt_t(pkt)
+ req = kres.request_t(req)
+ local ok, err = pcall(function () %s end)
+ if not ok then
+ print(err)
+ end
+ quit()
+end)']]
+-- Parse CLI arguments
+local function help()
+ name = 'kresd-query.lua'
+ print(string.format('Usage: %s [-t type] [-c class] [-C config] <name> <script>', name))
+ print('Execute a single-shot query and run a script on the result.')
+ print('There are two variables available: pkt (kres.pkt_t), req (kres.request_t)')
+ print('See modules README to learn about their APIs.')
+ print('')
+ print('Options:')
+ print('\t-h,--help ... print this help')
+ print('\t-t TYPE ... query for given type (default: A)')
+ print('\t-c CLASS ... query in given class (default: IN)')
+ print('\t-C config_str ... kresd-style config (default: -)')
+ print('Examples:')
+ print('\t'..name..' -t SOA cz "print(pkt:qname())" ... print response QNAME')
+end
+-- Parse CLI arguments
+if #arg < 2 then help() return 1 end
+local qtype, qclass, qname = 'A', 'IN', nil
+local config, scripts = '', {}
+k = 1 while k <= #arg do
+ local v = arg[k]
+ if v == '-h' or v == '--help' then
+ return help()
+ elseif v == '-C' then
+ k = k + 1
+ config = arg[k]
+ elseif v == '-c' then
+ k = k + 1
+ qclass = arg[k]:upper()
+ elseif v == '-t' then
+ k = k + 1
+ qtype = arg[k]:upper()
+ elseif v:byte() == string.byte('-') then
+ return help()
+ elseif not qname then
+ qname = v
+ else
+ table.insert(scripts, v)
+ end
+ k = k + 1
+end
+cli_cmd = string.format(cli_cmd, config, qname, qtype, qclass, table.concat(scripts, ' '))
+return os.execute(cli_cmd..' | '..cli_bin)
diff --git a/scripts/kresd.apparmor b/scripts/kresd.apparmor
new file mode 100644
index 0000000..ad6f911
--- /dev/null
+++ b/scripts/kresd.apparmor
@@ -0,0 +1,29 @@
+#include <tunables/global>
+
+/usr/sbin/kresd {
+ #include <abstractions/base>
+ #include <abstractions/p11-kit>
+ #include <abstractions/nameservice>
+ capability net_bind_service,
+ capability setgid,
+ capability setuid,
+ # seems to be needed during start to read /var/lib/knot-resolver
+ # while we still run as root.
+ capability dac_override,
+
+ network tcp,
+ network udp,
+
+ /proc/sys/net/core/somaxconn r,
+ /etc/knot-resolver/* r,
+ /var/lib/knot-resolver/ r,
+ /var/lib/knot-resolver/** rwlk,
+
+ # modules
+ /usr/lib{,64}/kdns_modules/*.lua r,
+ /usr/lib{,64}/kdns_modules/*.so rm,
+
+ # Site-specific additions and overrides. See local/README for details.
+ #include <local/usr.sbin.kresd>
+}
+
diff --git a/scripts/launch-test-instance.sh b/scripts/launch-test-instance.sh
new file mode 100755
index 0000000..8a93328
--- /dev/null
+++ b/scripts/launch-test-instance.sh
@@ -0,0 +1,26 @@
+#!/bin/sh -e
+export PATH="/usr/lib/ccache:$PATH"
+
+PORT=${1:-$((32767+$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -d' ' -f1) % 32768))}
+
+JOBS=$(cat /proc/cpuinfo | grep processor | wc -l)
+
+WORKDIR=${2:-$(mktemp -d /tmp/knot-resolver.XXXXXX)}
+
+PREFIX=${PREFIX:-$WORKDIR} make clean
+
+CFLAGS=${CFLAGS:-"-O2 -g3"} PREFIX=${PREFIX:-$WORKDIR} make -j ${JOBS} V=1
+
+PREFIX=${PREFIX:-$WORKDIR} make install
+
+install -d -m 0700 ${WORKDIR}/run/kresd
+
+echo "Launching Knot Resolver on port: ${PORT}"
+echo "To debug, use:"
+echo "dig +dnssec +multi +time=60 +retry=1 -p ${PORT} @::1"
+
+LD_LIBRARY_PATH=${WORKDIR}/lib ${WORKDIR}/sbin/kresd -a 127.0.0.1#${PORT} -a ::1#${PORT} -v -k ${ROOT_KEY:-/usr/share/dns/root.key} ${WORKDIR}/run/kresd
+
+if [ "${WORKDIR}" != "${2}" -a "${KEEP_WORKDIR}" != "yes" ]; then
+ rm -r ${WORKDIR}
+fi
diff --git a/scripts/luacov_gen_empty.sh b/scripts/luacov_gen_empty.sh
new file mode 100755
index 0000000..a224081
--- /dev/null
+++ b/scripts/luacov_gen_empty.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Generate stats file in luacov format indicating that files named on stdin
+# were not processed.
+#
+# Normally luacov does not know about files which were not loaded so
+# without this manual addition the files are missing in coverage report.
+
+# Usage:
+# $ luacov_gen_empty.sh < list_of_lua_files > luacov.empty_stats.out
+
+set -o errexit -o nounset
+IFS=$'\n'
+
+while read FILENAME
+do
+ echo -e "0:${FILENAME}\n "
+done
diff --git a/scripts/luacov_to_info.lua b/scripts/luacov_to_info.lua
new file mode 100755
index 0000000..372f5c0
--- /dev/null
+++ b/scripts/luacov_to_info.lua
@@ -0,0 +1,56 @@
+#!/usr/bin/env luajit
+
+local luacov = require('luacov')
+local ReporterBase = require('luacov.reporter').ReporterBase
+local LcovReporter = setmetatable({}, ReporterBase)
+LcovReporter.__index = LcovReporter
+
+function LcovReporter:on_new_file(filename)
+ self.finfo = self.current_files[filename] or {name=filename, coverage={}}
+end
+
+function LcovReporter:on_mis_line(_, lineno, _)
+ self.finfo.coverage[lineno] = self.finfo.coverage[lineno] or 0
+end
+
+function LcovReporter:on_hit_line(_, lineno, _, hits)
+ self.finfo.coverage[lineno] = (self.finfo.coverage[lineno] or 0) + hits
+end
+
+function LcovReporter:on_end_file()
+ self.current_files[self.finfo.name] = self.finfo
+ self.finfo = nil
+end
+
+-- Write out results in lcov format
+local function write_lcov_info(files)
+ for fname, finfo in pairs(files) do
+ local instrumented, nonzero = 0, 0
+ print('TN:')
+ print(string.format('SF:%s', fname))
+ for i, hits in pairs(finfo.coverage) do
+ print(string.format('DA:%d,%d', i, hits))
+ instrumented = instrumented + 1
+ if hits > 0 then
+ nonzero = nonzero + 1
+ end
+ end
+ print(string.format('LH:%d', nonzero))
+ print(string.format('LF:%d', instrumented))
+ print('end_of_record')
+ end
+end
+
+-- Accumulate total coverage
+local all_files = {}
+for _, fname in ipairs(arg) do
+ local conf = luacov.load_config()
+ conf.statsfile = fname
+ local reporter = assert(LcovReporter:new(conf))
+ reporter.current_files = all_files
+ reporter:run()
+ reporter:close()
+end
+
+-- Write results
+write_lcov_info(all_files)
diff --git a/scripts/make-archive.sh b/scripts/make-archive.sh
new file mode 100755
index 0000000..e3b1a97
--- /dev/null
+++ b/scripts/make-archive.sh
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+# Create a distribution tarball, like 'make dist' from autotools.
+cd "$(git rev-parse --show-toplevel)"
+ver=$(git describe | sed 's/^v//' | sed 's/-/\./g')
+test 0 -ne $(git status --porcelain | wc -l) && \
+ echo "Git working tree is dirty, make it clean first" && \
+ exit 1
+git submodule status --recursive | grep -q '^[^ ]' && \
+ echo "Git submodules are dirty, run: git submodule update --recursive --init" && \
+ exit 2
+
+# 'git ls-files --recurse-submodules' works only if modules are initialized
+name="knot-resolver-$ver"
+tar caf "$name.tar.xz" -h --no-recursion --transform "s|^|$name/|" -- $(git ls-files --recurse-submodules)
+echo "$name.tar.xz"
diff --git a/scripts/make-distrofiles.sh b/scripts/make-distrofiles.sh
new file mode 100755
index 0000000..957c067
--- /dev/null
+++ b/scripts/make-distrofiles.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+set -o errexit -o nounset -o xtrace
+
+# Run with -s to include *.symbols files.
+
+package=knot-resolver
+withsymbols=false
+
+while getopts "s" o; do
+ case "${o}" in
+ s)
+ withsymbols=true
+ ;;
+ *)
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+
+cd "$(git rev-parse --show-toplevel)"
+version=$(ls ${package}*.tar.xz | sed "s/${package}-\(.*\).tar.xz/\1/")
+
+# Check version for invalid characters
+if [[ $(echo "${version}" | grep '^[[:alnum:].]$') -ne 0 ]]; then
+ echo "Invalid version number: may contain only alphanumeric characters and dots"
+ exit 1
+fi
+
+# Fill in VERSION field in distribution specific files
+files="distro/rpm/${package}.spec distro/deb/changelog distro/arch/PKGBUILD"
+for file in ${files}; do
+ sed -i "s/__VERSION__/${version}/g" "${file}"
+done
+
+# Rename archive to debian format
+pkgname="${package}-${version}"
+debname="${package}_${version}.orig"
+mv "${pkgname}.tar.xz" "${debname}.tar.xz"
+
+# Prepare clean debian-specific directory
+tar -xf "${debname}.tar.xz"
+pushd "${pkgname}" > /dev/null
+cp -arL ../distro/deb debian
+
+# Optionally remove symbols file
+if [ "$withsymbols" = false ]; then
+ rm -f debian/*.symbols
+fi
+
+# Create debian archive and dsc
+dpkg-source -b .
+popd > /dev/null
diff --git a/scripts/make-srpm.sh b/scripts/make-srpm.sh
new file mode 100755
index 0000000..29fc8b3
--- /dev/null
+++ b/scripts/make-srpm.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -o errexit -o nounset -o xtrace
+
+# Create a source rpm for Fedora/EPEL
+
+cd "$(git rev-parse --show-toplevel)"
+scripts/make-archive.sh
+scripts/make-distrofiles.sh
+mv knot-resolver_*.orig.tar.xz distro/rpm/
+cd distro/rpm
+rpkg srpm --outdir .
+mv *.src.rpm ../../
+mv *.tar.xz ../../
diff --git a/scripts/map_install_src.lua b/scripts/map_install_src.lua
new file mode 100755
index 0000000..f4f0ed3
--- /dev/null
+++ b/scripts/map_install_src.lua
@@ -0,0 +1,167 @@
+#!/usr/bin/env luajit
+
+-- parse install commands from stdin
+-- input: PREFIX=... make install --dry-run --always-make
+-- output: <install path> <source path>
+-- (or sed commands if --sed was specified)
+
+output = 'list'
+if #arg > 1 or arg[1] == '-h' or arg[1] == '--help' then
+ print(string.format([[
+Read install commands and map install paths to paths in source directory.
+
+Usage:
+$ PREFIX=... make install --dry-run --always-make | %s
+
+Example output:
+/kresd/git/.local/lib/kdns_modules/policy.lua modules/policy/policy.lua
+
+Option --sed will produce output suitable as input suitable for sed.]],
+ arg[0]))
+ os.exit(1)
+elseif #arg == 0 then
+ output = 'list'
+elseif arg[1] == '--sed' then
+ output = 'sed'
+else
+ print('Invalid arguments. See --help.')
+ os.exit(2)
+end
+
+-- remove double // from paths and remove trailing /
+function normalize_path(path)
+ assert(path)
+ repeat
+ path, changes = path:gsub('//', '/')
+ until changes == 0
+ return path:gsub('/$', '')
+end
+
+function is_opt(word)
+ return word:match('^-')
+end
+
+-- opts requiring additional argument to be skipped
+local ignored_opts_with_arg = {
+ ['--backup'] = true,
+ ['-g'] = true,
+ ['--group'] = true,
+ ['-m'] = true,
+ ['--mode'] = true,
+ ['-o'] = true,
+ ['--owner'] = true,
+ ['--strip-program'] = true,
+ ['--suffix'] = true,
+}
+
+-- state machine junctions caused by --opts
+-- returns: new state (expect, mode) and target name if any
+function parse_opts(word, expect, mode)
+ if word == '--' then
+ return 'names', mode, nil -- no options anymore
+ elseif word == '-d' or word == '--directory' then
+ return 'opt_or_name', 'newdir', nil
+ elseif word == '-t' or word == '--target-directory' then
+ return 'targetdir', mode, nil
+ elseif word:match('^--target-directory=') then
+ return 'opt_or_name', mode, string.sub(word, 20)
+ elseif ignored_opts_with_arg[word] then
+ return 'ignore', mode, nil -- ignore next word
+ else
+ return expect, mode, nil -- unhandled opt
+ end
+end
+
+
+-- cmd: complete install command line: install -m 0644 -t dest src1 src2
+-- dirs: names known to be directories: name => true
+-- returns: updated dirs
+function process_cmd(cmd, dirs)
+ -- print('# ' .. cmd)
+ sanity_check(cmd)
+ local expect = 'install'
+ local mode = 'copy' -- copy or newdir
+ local target -- last argument or argument for install -t
+ local names = {} -- non-option arguments
+
+ for word in cmd:gmatch('%S+') do
+ if expect == 'install' then -- parsing 'install'
+ assert(word == 'install')
+ expect = 'opt_or_name'
+ elseif expect == 'opt_or_name' then
+ if is_opt(word) then
+ expect, mode, newtarget = parse_opts(word, expect, mode)
+ target = newtarget or target
+ else
+ if mode == 'copy' then
+ table.insert(names, word)
+ elseif mode == 'newdir' then
+ local path = normalize_path(word)
+ dirs[path] = true
+ else
+ assert(false, 'bad mode')
+ end
+ end
+ elseif expect == 'targetdir' then
+ local path = normalize_path(word)
+ dirs[path] = true
+ target = word
+ expect = 'opt_or_name'
+ elseif expect == 'names' then
+ table.insert(names, word)
+ elseif expect == 'ignore' then
+ expect = 'opt_or_name'
+ else
+ assert(false, 'bad expect')
+ end
+ end
+ if mode == 'newdir' then
+ -- no mapping to print, this cmd just created directory
+ return dirs
+ end
+
+ if not target then -- last argument is the target
+ target = table.remove(names)
+ end
+ assert(target, 'fatal: no target in install cmd')
+ target = normalize_path(target)
+
+ for _, name in pairs(names) do
+ basename = string.gsub(name, "(.*/)(.*)", "%2")
+ if not dirs[target] then
+ print('fatal: target directory "' .. target .. '" was not created yet!')
+ os.exit(2)
+ end
+ -- mapping installed name -> source name
+ if output == 'list' then
+ print(target .. '/' .. basename, name)
+ elseif output == 'sed' then
+ print(string.format([[s`%s`%s`g]],
+ target .. '/' .. basename, name))
+ else
+ assert(false, 'unsupported output')
+ end
+ end
+ return dirs
+end
+
+function sanity_check(cmd)
+ -- shell quotation is not supported
+ assert(not cmd:match('"'), 'quotes " are not supported')
+ assert(not cmd:match("'"), "quotes ' are not supported")
+ assert(not cmd:match('\\'), "escapes like \\ are not supported")
+ assert(cmd:match('^install%s'), 'not an install command')
+end
+
+-- remember directories created by install -d so we can expand relative paths
+local dirs = {}
+while true do
+ local cmd = io.read("*line")
+ if not cmd then
+ break
+ end
+ local isinstall = cmd:match('^install%s')
+ if isinstall then
+ dirs = process_cmd(cmd, dirs)
+ end
+end
diff --git a/scripts/obs-testbuild.sh b/scripts/obs-testbuild.sh
new file mode 100755
index 0000000..f44cecc
--- /dev/null
+++ b/scripts/obs-testbuild.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Builds the checked out version in knot-dns-testing OBS repository
+
+set -o errexit -o nounset -o xtrace
+
+force=false
+
+# Read options
+while getopts "f" o; do
+ case "${o}" in
+ f)
+ force=true
+ ;;
+ *)
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+# Clean working tree
+if [[ $(git status --porcelain | wc -l) -ne 0 ]]; then
+ if [ "$force" = false ]; then
+ echo "working tree dirty. force clean with '-f'"
+ exit 1
+ fi
+ git clean -dfx
+ git reset --hard
+fi
+
+# Create tarball
+scripts/make-archive.sh
+
+# Submit to OBS
+scripts/make-distrofiles.sh -s
+scripts/build-in-obs.sh knot-resolver-testing
+
+echo "Check results at https://build.opensuse.org/package/show/home:CZ-NIC:knot-resolver-testing/knot-resolver"