Verified Commit fd85933a authored by Andrej Shadura's avatar Andrej Shadura

Add 'common/' from commit '159717ce'

git-subtree-dir: common
git-subtree-mainline: 169aad26
git-subtree-split: 159717ce
parents 169aad26 159717ce
Common files shared accross apertis tests.
Those files are shared through a git subtree which is included by all test projects.
Use `common-subtree.sh add` to add the subtree in a tests repository.
For example:
```
# Retrieve the common-subtree.sh script, we use `git`, but could also use `wget`
$ git clone git@gitlab.apertis.org:tests/common
# Clone the test repository if needed
$ git clone git@gitlab.apertis.org:tests/iptables-basic
# Enter the test directory
$ cd iptables-basic
# In the test directory, add the common git subtree
$ ../common/common-subtree.sh add
# A new commit has been created, push the test with the subtree initialized
$ git push origin
```
If a script is supposed to be in the common/ directory, it should be commited in the
[common repository](https://gitlab.apertis.org/tests/common), and then it can be pulled
into the git subtree using the command:
```
$ ../common/common-subtree.sh pull
```
# Need to source this file.
# Pass in audit string, returns formatted block as `aa_log_extract_tokens.pl`
# would have provided. Need to return:
#
# profile:/usr/bin/busctl
# sdmode:REJECTING
# denied_mask:r
# operation:open
# name:/proc/879/stat
# request_mask:r
#
# Note: apparmor uses the term "DENIED" rather than "REJECTING", but our
# expected values want "REJECTING", so tweak for now.
apparmor_parse_journal() {
awk -v event=$2 '
{
for (i = 1; i <= NF; i++) {
n = index($i, "=");
if(n) {
vars[substr($i, 1, n - 1)] = substr($i, n + 1)
# Strip quotes
gsub("\"","",vars[substr($i, 1, n - 1)])
}
}
}
{
if (vars["apparmor"] == event) {
print "===="
print "profile:"vars["profile"]
# Match old nomenclature
if (vars["apparmor"] == "DENIED") {
vars["apparmor"] = "REJECTING"
}
print "sdmode:"vars["apparmor"]
print "denied_mask:"vars["denied_mask"]
print "operation:"vars["operation"]
print "name:"vars["name"]
print "request_mask:"vars["requested_mask"]
}
}
' $1
}
#!/bin/sh
usage () {
echo "usage: $0 [add|pull]"
echo ""
echo "'$0 add' adds the common subtree to a tests repository"
echo "'$0 pull' is used to update the common subtree in tests"
}
case $1 in
pull)
git subtree pull -P common git@gitlab.apertis.org:tests/common.git master
;;
add)
git subtree add -P common git@gitlab.apertis.org:tests/common.git master
;;
*) usage;;
esac
# Source me!
# I need ROOTDIR, PN and PV to be defined! (see bottom of file)
# vim: set sts=4 sw=4 et :
# Detect whether stdout is to a terminal
if tty -s <&1; then
IS_TTY="true"
fi
is_tty() {
[ "${IS_TTY}" = true ] && return 0
return 1
}
##########
# Murder #
##########
_kill_daemons() {
pkill dconf-service >/dev/null 2>&1 || true
return 0 # Never fail
}
##########
# Output #
##########
cry_n() {
if is_tty; then
# Cry tears of blood
/bin/echo -n -e "\033[01;31m#\033[00m $@"
else
/bin/echo -n -e "# $@"
fi
}
cry() {
cry_n "$@"
echo
}
say_n() {
if is_tty; then
# Speak in green
/bin/echo -n -e "\033[01;32m#\033[00m $@"
else
/bin/echo -n -e "# $@"
fi
}
say() {
say_n "$@"
echo
}
whine_n() {
if is_tty; then
# Whine in yellow
/bin/echo -n -e "\033[01;33m#\033[00m $@"
else
/bin/echo -n -e "# $@"
fi
}
whine() {
whine_n "$@"
echo
}
echo_red() {
if is_tty; then
# Print text in red, without an implicit newline
/bin/echo -n -e "\033[01;31m$@\033[00m"
else
/bin/echo -n -e "$@"
fi
}
echo_green() {
if is_tty; then
# Print text in green, without an implicit newline
/bin/echo -n -e "\033[01;32m$@\033[00m"
else
/bin/echo -n -e "$@"
fi
}
###################
# Status Messages #
###################
test_success() {
say "All tests PASSED successfully!"
_kill_daemons
trap - ERR 2>/dev/null || true
trap - EXIT
exit 0
}
test_failure() {
[ $? -eq 0 ] && exit
cry "Tests FAILED!"
_kill_daemons
whine "Work directory was: ${WORKDIR}"
exit 0
}
setup_success() {
say "Test setup successfully!"
_kill_daemons
trap - ERR 2>/dev/null || true
trap - EXIT
return 0
}
setup_failure() {
[ $? -eq 0 ] && exit
cry "Test setup failed!"
_kill_daemons
exit 1
}
#############
# Utilities #
#############
create_temp_workdir() {
local tempdir="$(mktemp -d -p ${WORKDIR})"
echo "${tempdir}" 1>&2
_realpath "${tempdir}"
}
# Takes an IFS delimited list of files from stdin, checks if each exists, and
# passes it on if it does. If the file doesn't exist, it prefixes the filename
# with "DNE", which is caught by _src_test, which then marks it as a failed test
check_file_exists_tee() {
while read i; do
if [ -e "$i" ]; then
echo "$i"
else
echo "DNE: $i"
fi
done
}
check_not_root() {
if [ "$(id -ru)" = 0 ]; then
cry "Do not run this test as root!"
return 1
fi
return 0
}
check_have_root() {
if [ "$(id -ru)" != 0 ]; then
cry "Need root to run this test successfully!"
return 1
fi
return 0
}
arch_is_arm() {
case "$(uname -m)" in
arm*)
return 0
;;
esac
return 1
}
# Check if a session bus is running otherwise fail
ensure_dbus_session() {
local dbus_socket
dbus_socket="/run/user/$(id -ru)/bus"
if [ ! -e "${dbus_socket}" ]; then
cry "Could not find session bus..."
return 1
fi
return 0
}
######################
# Internal functions #
######################
# Sleep, unless specified otherwise
_sleep() {
if [ -z "${QUICK}" ]; then
/bin/sleep "$@"
else
/bin/sleep 0.2
fi
}
# Run external utilities with this
# All output is suppressed by default as LAVA doesn't cope well with very large logs.
# Use DEBUG=1 to enable all output when running tests locally on developer devices.
# Short-circuiting with a return is needed to prevent unexpected exiting due to
# a non-zero return code when set -e is enabled.
_run_cmd() {
if [ "${DEBUG}" = 1 ]
then
"$@" || return
else
"$@" >/dev/null 2>&1 || return
fi
}
_expect_pass() {
_run_cmd "$@"
}
_expect_fail() {
! _run_cmd "$@"
}
_src_test() {
# Reads a list of tests to run via stdin, and executes them one by one
# All these are supposed to pass
local i
local failed=
local expect=$1
local prefix=""
shift
if [ -n "${APERTIS_TESTS_NAME_PREFIX}" ]; then
prefix="${APERTIS_TESTS_NAME_PREFIX}"
fi
while read i; do
case $i in
[#]*)
echo_red "${prefix}$i: skip\n"
continue
;;
# See check_file_exists_tee()
DNE*)
echo_red "${prefix}$i: fail\n"
whine "Got an invalid executable name '$i'!"
failed="$failed $i"
continue
;;
esac
say "Running test '$i' ..."
if ! "$expect" "$i" "$@"; then
failed="$failed $i"
echo_red "${prefix}$i: fail\n"
else
echo_green "${prefix}$i: pass\n"
fi
done
if [ -n "$failed" ]; then
whine "The following tests failed: "
for i in $failed; do
whine "\t$i"
done
return 1
fi
}
# Let's not depend on realpath; we don't need it
_realpath() {
cd "$1"
echo "$PWD"
}
##########
# Phases #
##########
src_test_pass() {
_src_test _expect_pass "$@"
}
src_test_fail() {
_src_test _expect_fail "$@"
}
src_unpack() {
mkdir -p "${WORKDIR}"
}
src_cleanup() {
rm -rf "${WORKDIR}"
}
src_copy() {
# Copy $1 $2 .. $N-1 to $N
# Essentially just a wrapper around `cp` right now
cp -v -L -r "$@" 1>&2
}
src_copy_contents() {
# Copy the contents of $1 to $2
# FIXME: This ignores dot-files. Use rsync or something?
cp -v -L -r "$1"/* "$2" 1>&2
}
#############
# Variables #
#############
# Fix this to not flood /var/tmp with temporary directories
BASEWORKDIR="/var/tmp/chaiwala-tests"
mkdir -p "${BASEWORKDIR}"
WORKDIR="$(mktemp -d -p ${BASEWORKDIR} "$(date +%Y%m%d-%H%M%S)-XXXXXXXXXX")"
# Tests might be run as chaiwala, or as root, or some other user
# Everyone should be able to write here
chown 1000 "${BASEWORKDIR}"
chmod 777 "${BASEWORKDIR}" || true
chmod 777 "${WORKDIR}" || true
sync
# Wrappers for external commands used
WGET="${WGET:-wget -c}"
# We disable apt-get, and just do a pass-through because these tests are
# integrated into LAVA now
#APT_GET="$(type -P true)"
GDBUS="${GDBUS:-gdbus}"
# 0 = no output
# 1 = stderr
# 2 = stdout + stderr
DEBUG="${DEBUG:-0}"
# This file should be symlinked inside your test directory,
# and sourced from config.sh
# It will be modified and copied to the install directory by make
. "${TESTDIR}/common/common.sh"
## These variables are properties of the image itself
LIBEXECDIR="${LIBEXECDIR:-/usr/lib}"
## These variables get modified by `make` during install
# Directory where scripts and other non-binary data is installed
TESTDATADIR="${TESTDIR}"
# Binary executable install directory
TESTLIBDIR="${TESTDIR}"
REALTESTDIR="$(_realpath ${TESTDIR})"
# Directory where test data/resources are installed
RESOURCE_DIR="${REALTESTDIR}/common/resources"
## These are derived from the above
MEDIA_RESOURCE_DIR="${RESOURCE_DIR}/media"
#!/bin/sh
#
# Copyright © 2018 Collabora Ltd.
#
# Based on python version of run-aa-test
#
# 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 http://mozilla.org/MPL/2.0/.
PWD=$(cd $(dirname $0); pwd; cd - >/dev/null 2>&1)
. ${PWD}/common-apparmor.sh
ALTERNATIVE_SEPARATOR="## alternative ##"
END=2
case $(echo ${LAUNCH_DBUS} | tr [A-Z] [a-z]) in
0 | no | false)
LAUNCH_DBUS="False"
;;
*)
LAUNCH_DBUS="True"
esac
case $(echo ${RUN_AS_USER} | tr [A-Z] [a-z]) in
0 | no | false)
RUN_AS_USER="False"
;;
*)
RUN_AS_USER="True"
esac
CHAIWALA_UID=1000
CHAIWALA_USER="user"
# Check parameters
if [ $# -lt 2 ]; then
echo "Usage: run-aa-test <expectation-file> <command> <argument-1> <argument-2> …"
echo "\"export LAUNCH_DBUS=no\" in the test script to not launch a dbus session."
echo "\"export RUN_AS_USER=no\" in the test script to not run as ${CHAIWALA_USER}"
exit 1
fi
EXPECT_FILE=$1
shift
if [ ! -r ${EXPECT_FILE} ]; then
echo "Cannot read specified expectation file: ${EXPECT_FILE}"
exit 1
fi
if [ ! -x $1 ]; then
echo "Cannot execute specified test executable: $1"
exit 1
fi
# typically "normal.expected" or "malicious.expected"
TEST_TITLE=$( basename ${EXPECT_FILE} )
# Touch .bash_history, which we use in some tests, if it's not there.
bash_history="/home/${CHAIWALA_USER}/.bash_history"
if [ ! -r ${bash_history} ]; then
sudo -u ${CHAIWALA_USER} touch ${bash_history}
RET=$?
if [ "$RET" != "0" ]; then
echo "Failed to create .bash_history: $RET"
exit 1
fi
fi
# Create a temporary directory for files
TMP_DIR=$(mktemp -d)
# Log start time
START_TIME=$(date +"%F %T")
if [ "${LAUNCH_DBUS}" = "True" ]; then
# Start a new D-Bus session for this test
CMD="dbus-run-session -- $*"
else
CMD="$*"
fi
CMDLINE="${PWD}/run-test-in-systemd"
if [ ! -x $CMDLINE ]; then
echo "common/run-test-in-systemd not found"
exit 1
fi
CMDLINE="${CMDLINE}"
if [ "${RUN_AA_TEST_TIMEOUT}" != "" ]; then
CMDLINE="${CMDLINE} --timeout=${RUN_AA_TEST_TIMEOUT}"
fi
if [ "${RUN_AS_USER}" = "True" ]; then
CMDLINE="${CMDLINE} --user=${CHAIWALA_UID}"
else
CMDLINE="${CMDLINE} --system"
fi
CMDLINE="${CMDLINE} ${CMD}"
echo "#=== running test script: ${CMDLINE} ==="
setsid ${CMDLINE}
RET=$?
echo "#--- end of test script, status: ${RET}"
if [ "${RET}" = "0" ]; then
echo "${TEST_TITLE}_underlying_tests: pass"
else
echo "# ${CMDLINE} exited ${RET}"
# typically "normal.expected_underlying_tests: fail"
echo "${TEST_TITLE}_underlying_tests: fail"
exit 1
fi
# Give journal time to log the entries.
sleep 3
# Get audit information from journal
AUDIT_FILE=${TMP_DIR}/AUDIT
journalctl -S "${START_TIME}" -t audit -o cat > ${AUDIT_FILE}
echo "#=== ${TEST_TITLE} ==="
echo "#---8<--- raw apparmor output from journal"
cat ${AUDIT_FILE} | sed 's/^/# /'
echo "#--->8---"
echo "#---8<--- expected parsed apparmor output from journal"
cat ${EXPECT_FILE} | sed 's/^/# /'
echo "#--->8---"
csplit ${EXPECT_FILE} -f ${TMP_DIR}/EXPECT -b "%d" "/^${ALTERNATIVE_SEPARATOR}$/" {*}
# Old versions of csplit don't provide "--suppress-matched", strip separator separately
for FILE in ${TMP_DIR}/EXPECT*; do
sed -i "/^${ALTERNATIVE_SEPARATOR}$/d" ${FILE}
done
PARSE_FILE="${TMP_DIR}/PARSE"
# TODO: There is potential for other processes to cause messages to appear in
# the journal that may lead to false failures. If this is found to be an
# issue in practice, then additional filtering of results may be required
apparmor_parse_journal ${AUDIT_FILE} DENIED > ${PARSE_FILE}
echo "#---8<--- actual parsed apparmor output from journal"
cat ${PARSE_FILE} | sed 's/^/# /'
echo "#--->8---"
MATCH_EXPECTATION="False"
# We might have alternative expectations, take that into consideration.
OUTPUT_MD5=$( cat ${PARSE_FILE} | md5sum )
COUNT=$( ls -1 ${TMP_DIR}/EXPECT* | wc -l )
NUM=0
while [ $((${NUM} < ${COUNT})) = 1 ]; do
EXPECTED_MD5=$( cat ${TMP_DIR}/EXPECT${NUM} | md5sum )
if [ "${OUTPUT_MD5}" = "${EXPECTED_MD5}" ]; then
echo "# audit log matches alternative expectation ${NUM}/${COUNT}"
MATCH_EXPECTATION="True"
fi
NUM=$((${NUM}+1))
done
if [ "${MATCH_EXPECTATION}" = "True" ]; then
echo "${TEST_TITLE}: pass"
else
echo "#---8<--- diff"
diff -urN ${TMP_DIR}/EXPECT${NUM} ${PARSE_FILE}
echo "#--->8---"
echo "${TEST_TITLE}: fail"
exit 1
fi
exit 0
#!/bin/sh
# run-test-in-systemd.sh — run a LAVA test as a systemd unit
#
# Copyright © 2015, 2016, 2017 Collabora Ltd.
#
# 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 http://mozilla.org/MPL/2.0/.
usage='
Usage:
run-test-in-systemd [OPTIONS...] [--] COMMAND [ARGS...]
Run COMMAND ARGS as a systemd unit
Options:
--system|--user=UID Run as a system or user service
(default: current user, or --system if run as root)
--name=NAME Use NAME as the test-case name, escaping special
characters (default: COMMAND ARGS)
--basename Use the non-directory part of COMMAND as the test-case
name for LAVA, stripping extensions .pl, .py and/or
.sh and escaping special characters
--timeout=EXPR Set a timeout (systemd syntax is allowed, e.g. 300,
300s, 5min, 300000ms are equivalent; default 15min)
--chdir=DIRNAME Change current working directory to DIRNAME
--exit-status Exit with the status of COMMAND
--no-lava Do not integrate with LAVA (implies --exit-status)
--debug Show journal messages
--full-debug Show full journal logs
'
set -e
as_target_user=
debug=
full_debug=
journalctl="journalctl --no-pager --full"
lava_runes=1
name=""
progname="$(basename "$0")"
systemctl="systemctl"
target_user=
time_adverb=""
timeout=15min
uid="$(id -u)"
use_basename=
use_exit_status=
working_dir="$(pwd)"
debug () {
[ -z "${debug}" ] || echo "# ${progname}: $*" >&2
}
die () {
echo "${progname}: ERROR: $*" >&2
exit 1
}
systemd_escape_argument () {
# Escape an argument for ExecStart
# - escape % as %%, $ as $$ and \ as \\
# - escape single and double quotes
# - escape control characters, delete and non-ASCII like \xff
# I can't work out how to do the latter in sh so I'm resorting to Perl
perl -e '$_ = $ARGV[0];
s/([\\%\$])/$1$1/g;
s/([\x22\x27])/\\$1/g;
s/([^\x20-\x7e])/sprintf(q(\\x%02x), ord $1)/ge;
print qq("$_");
' -- "$1"
}
if [ "x${uid}" != x0 ]
then
target_user="${uid}"
journalctl="sudo ${journalctl}"
fi
if which busybox >/dev/null; then
time_adverb="busybox time"
elif which time >/dev/null; then
time_adverb="time"
else
time_adverb=""
fi
if ! which "lava-test-case" >/dev/null; then
lava_runes=
use_exit_status=1
fi
# -o + means stop parsing at the first non-option argument, so we can do
# things like: run-test-in-systemd sh -c 'echo hello'
args="$(getopt \
-o '+h' \
-l "basename,chdir:,debug,exit-status,full-debug,help,name:,no-lava,system,timeout:,user:" \
-n "${progname}" -- "$@")"
eval set -- "${args}"
while [ "$#" -gt 0 ]; do
case "$1" in
(--basename)
use_basename=1
shift
;;
(--debug)
debug=1
shift
;;
(--exit-status)
use_exit_status=1
shift
;;
(--full-debug)
full_debug=1
shift
;;
(--help|-h)
echo "${usage}"
exit 0
;;
(--name)
name="$2"
shift 2
;;
(--no-lava)
lava_runes=
use_exit_status=1
shift
;;
(--system)
target_user=
as_uid=
shift
;;
(--timeout)