__dest=/
__pb_mount_dir=/var/petitboot/mnt/dev/
+plugin_abi=1
plugin_ext=pb-plugin
plugin_meta=pb-plugin.conf
plugin_meta_dir=etc/preboot-plugins/
Where <command> is one of:
install <FILE|URL> - install plugin from FILE/URL
- scan - look for available plugins on attached devices
+ scan <DIR> - look for available plugins on attached devices
create <DIR> - create a new plugin archive from DIR
+ lint <FILE> - perform a pre-distribution check on FILE
EOF
}
echo " (version $PLUGIN_VERSION)"
}
+parse_meta()
+{
+ local file name value IFS
+
+ file=$1
+
+ IFS='='
+ while read -r name value
+ do
+ # Ensure we have a sensible variable name
+ echo "$name" | grep -q '^PLUGIN_[A-Z_]*$' || continue
+
+ # we know that $name has no quoting/expansion chars, but we
+ # may need to do some basic surrounding-quote removal for
+ # $value, without evaluating it
+ value=$(echo "$value" | sed "s/^\([\"']\)\(.*\)\1\$/\2/g")
+
+ export $name="$value"
+ done < $file
+}
+
+# How the ABI versioning works:
+#
+# - This script has an ABI defined ($plugin_abi)
+#
+# - Plugins have a current ABI number ($PLUGIN_ABI), and a minimum supported
+# ABI number ($PLUGIN_ABI_MIN).
+#
+# - A plugin is OK to run if:
+# - the plugin's ABI matches the script ABI, or
+# - the plugin's minimum ABI is lower than or equal to the script ABI
+plugin_abi_check()
+{
+ [ -n "$PLUGIN_ABI" ] &&
+ ( [ $PLUGIN_ABI -eq $plugin_abi ] ||
+ [ $PLUGIN_ABI_MIN -le $plugin_abi ] )
+}
+
do_wrap()
{
local base binary dir
do_install()
{
- local url name file __dest
+ local url name file __dest auto
+
+ if [ "$1" == "auto" ]; then
+ auto=y
+ shift;
+ fi
url=$1
echo
sha256sum "$file" | cut -f1 -d' '
echo
- echo "Do you want to install this plugin? (y/N)"
- read resp
- case $resp in
- [yY]|[yY][eE][sS])
- ;;
- *)
- echo "Cancelled"
- exit 0
- ;;
- esac
+ if [ -z "$auto" ]
+ then
+ echo "Do you want to install this plugin? (y/N)"
+ read resp
+
+ case $resp in
+ [yY]|[yY][eE][sS])
+ ;;
+ *)
+ echo "Cancelled"
+ exit 0
+ ;;
+ esac
+ fi
__dest=$(mktemp -d)
gunzip -c "$file" | ( cd $__dest && cpio -i -d)
exit 1
fi
- . $__dest/$plugin_meta_path
+ parse_meta $__dest/$plugin_meta_path
+
+ if ! plugin_abi_check
+ then
+ echo "Plugin at $url is incompatible with this firmware," \
+ "exiting."
+ rm -rf $__dest
+ exit 1
+ fi
for binary in ${PLUGIN_EXECUTABLES}
do
__create_wrapper "$__dest" "$binary"
done
+ pb-event plugin@local \
+ name=$PLUGIN_NAME id=$PLUGIN_ID version=$PLUGIN_VERSION \
+ vendor=$PLUGIN_VENDOR vendor_id=$PLUGIN_VENDOR_ID \
+ date=$PLUGIN_DATE executables="$PLUGIN_EXECUTABLES" \
+ source_file=$url installed="yes"
+
echo "Plugin installed"
plugin_info
}
fi
(
- . $__meta_tmp/$plugin_meta_path
+ parse_meta $__meta_tmp/$plugin_meta_path
+
+ plugin_abi_check || exit 1
printf "Plugin found on %s:\n" $dev
plugin_info
printf "\n"
printf "To run this plugin:\n"
- printf " $0 run $plugin_path\n"
+ printf " $0 install $plugin_path\n"
printf "\n"
+
+ pb-event plugin@$dev name=$PLUGIN_NAME \
+ path=$plugin_path installed="no"
)
+ if [ $? = 0 ]
+ then
+ found=1
+ fi
rm -rf $__meta_tmp
- found=1
done
}
do_scan()
{
- local found mnt
+ local found mnt dev locations
found=0
- for mnt in $__pb_mount_dir/*
+ dev=$1
+
+ if [ -z $dev ]; then
+ locations=$__pb_mount_dir/*
+ else
+ echo "Scanning device $dev"
+ locations=$dev
+ fi
+
+ for mnt in $locations
do
do_scan_mount $mnt
done
mkdir -p $(dirname $file)
cat <<EOF > $file
+PLUGIN_ABI='$plugin_abi'
+PLUGIN_ABI_MIN='1'
PLUGIN_VENDOR='$vendorname'
PLUGIN_VENDOR_ID='$vendorshortname'
PLUGIN_NAME='$pluginname'
fi
# Sanity check metadata file
- . $meta_file
- if [ ! -n "$PLUGIN_VENDOR" ]
- then
- echo "error: no PLUGIN_VENDOR defined in metadata" &>2
- exit 1
- fi
- if [ ! -n "$PLUGIN_VENDOR_ID" ]
- then
- echo "error: no PLUGIN_VENDOR_ID defined in metadata" &>2
- exit 1
- fi
- if [ ! -n "$PLUGIN_NAME" ]
- then
- echo "error: no PLUGIN_NAME defined in metadata" &>2
- exit 1
- fi
- if [ ! -n "$PLUGIN_ID" ]
- then
- echo "error: no PLUGIN_ID defined in metadata" &>2
- exit 1
- fi
- if [ ! -n "$PLUGIN_VERSION" ]
- then
- echo "error: no PLUGIN_VERSION defined in metadata" &>2
- exit 1
- fi
- if [ ! -n "$PLUGIN_DATE" ]
- then
- echo "error: no PLUGIN_DATE defined in metadata" &>2
- exit 1
- fi
- if [ ! -n "$PLUGIN_EXECUTABLES" ]
+ parse_meta $meta_file
+
+ errors=0
+ warnings=0
+
+ lint_metadata
+
+ if [ $errors -ne 0 ]
then
- echo "error: no PLUGIN_EXECUTABLES defined in metadata" &>2
exit 1
fi
EOF
}
+lint_fatal()
+{
+ echo "fatal:" "$@"
+ [ -d "$__dest" ] && rm -rf "$__dest"
+ exit 1
+}
+
+lint_err()
+{
+ echo "error:" "$@"
+ errors=$(($errors+1))
+}
+
+lint_warn()
+{
+ echo "warning:" "$@"
+ warnings=$(($warnings+1))
+}
+
+lint_metadata()
+{
+ [ -n "$PLUGIN_ABI" ] ||
+ lint_err "no PLUGIN_ABI defined in metadata"
+
+ printf '%s' "$PLUGIN_ABI" | grep -q '[^0-9]' &&
+ lint_err "PLUGIN_ABI has non-numeric characters"
+
+ [ -n "$PLUGIN_ABI_MIN" ] ||
+ lint_err "no PLUGIN_ABI_MIN defined in metadata"
+
+ printf '%s' "$PLUGIN_ABI_MIN" | grep -q '[^0-9]' &&
+ lint_err "PLUGIN_ABI_MIN has non-numeric characters"
+
+ [ "$PLUGIN_ABI" = "$plugin_abi" ] ||
+ lint_warn "PLUGIN_ABI (=$PLUGIN_ABI) is not $plugin_abi"
+
+ [ -n "$PLUGIN_VENDOR" ] ||
+ lint_err "no PLUGIN_VENDOR defined in metadata"
+
+ [ -n "$PLUGIN_VENDOR_ID" ] ||
+ lint_err "no PLUGIN_VENDOR_ID defined in metadata"
+
+ printf '%s' "$PLUGIN_VENDOR_ID" | grep -q '[^a-z0-9-]' &&
+ lint_err "PLUGIN_VENDOR_ID should only contain lowercase" \
+ "alphanumerics and hyphens"
+
+ [ -n "$PLUGIN_NAME" ] ||
+ lint_err "no PLUGIN_NAME defined in metadata"
+
+ [ -n "$PLUGIN_ID" ] ||
+ lint_err "no PLUGIN_ID defined in metadata"
+
+ printf '%s' "$PLUGIN_ID" | grep -q '[^a-z0-9-]' &&
+ lint_err "PLUGIN_ID should only contain lowercase" \
+ "alphanumerics and hyphens"
+
+ [ "$PLUGIN_VERSION" ] ||
+ lint_err "no PLUGIN_VERSION defined in metadata"
+
+ [ -n "$PLUGIN_DATE" ] ||
+ lint_err "no PLUGIN_DATE defined in metadata"
+
+ [ -n "$PLUGIN_EXECUTABLES" ] ||
+ lint_err "no PLUGIN_EXECUTABLES defined in metadata"
+}
+
+do_lint()
+{
+ local plugin_file errors warnings __dest executable dir
+
+ plugin_file=$1
+ errors=0
+ warnings=0
+ __dest=
+
+ [ "${plugin_file##*.}" = $plugin_ext ] ||
+ lint_err "Plugin file does not end with $plugin_ext"
+
+ gunzip -c "$plugin_file" > /dev/null 2>&1 ||
+ lint_fatal "Plugin can't be gunzipped"
+
+ gunzip -c "$plugin_file" 2>/dev/null | cpio -t >/dev/null 2>&1 ||
+ lint_fatal "Plugin can't be cpioed"
+
+ __dest=$(mktemp -d)
+ gunzip -c "$plugin_file" | ( cd $__dest && cpio -i -d 2>/dev/null)
+
+ [ -e "$__dest/$plugin_meta_path" ] ||
+ lint_fatal "No metadata file present (expecting" \
+ "$plugin_meta_path)"
+
+ parse_meta "$__dest/$plugin_meta_path"
+ lint_metadata
+
+ for executable in ${PLUGIN_EXECUTABLES}
+ do
+ exec_path="$__dest/$executable"
+ [ -e "$exec_path" ] || {
+ lint_err "PLUGIN_EXECUTABLES item $executable" \
+ "doesn't exist"
+ continue
+ }
+
+ [ -x "$exec_path" ] ||
+ lint_err "PLUGIN_EXECUTABLES item $executable" \
+ "isn't executable"
+ done
+
+ for dir in dev sys proc var
+ do
+ [ -e "$__dest/$dir" ] || continue
+
+ [ -d "$__dest/$dir" ] ||
+ lint_err "/$dir exists, but isn't a directory"
+
+ [ "$(find $__dest/$dir -mindepth 1)" ] &&
+ lint_warn "/$dir contains files/directories," \
+ "these will be lost during chroot setup"
+ done
+
+ printf '%s: %d errors, %d warnings\n' $plugin_file $errors $warnings
+ rm -rf $__dest
+ [ $errors = 0 ]
+}
+
test_http_download()
{
local tmp ref
cmp -s "$ref" "$tmp"
}
+test_abi_check()
+{
+ (
+ plugin_abi=$1
+ PLUGIN_ABI=$2
+ PLUGIN_ABI_MIN=$3
+ plugin_abi_check
+ )
+}
+
test_scan()
{
__pb_mount_dir="$test_tmpdir/mnt"
mnt_dir="$__pb_mount_dir/sda"
mkdir -p $mnt_dir/$plugin_meta_dir
(
+ echo "PLUGIN_ABI=$plugin_abi"
echo "PLUGIN_NAME=test"
echo "PLUGIN_VERSION=1"
echo "PLUGIN_EXECUTABLES=/bin/sh"
do
mkdir -p $mnt_dir/$plugin_meta_dir
(
+ echo "PLUGIN_ABI=$plugin_abi"
echo "PLUGIN_NAME=test-$i"
echo "PLUGIN_VERSION=1"
echo "PLUGIN_EXECUTABLES=/bin/sh"
grep -q 'test-1' $outfile && grep -q 'test-2' $outfile
}
+test_scan_wrongabi()
+{
+ __pb_mount_dir="$test_tmpdir/mnt"
+ mnt_dir="$__pb_mount_dir/sda"
+ mkdir -p $mnt_dir/$plugin_meta_dir
+ (
+ echo "PLUGIN_ABI=$(($plugin_abi + 1))"
+ echo "PLUGIN_ABI_MIN=$(($plugin_abi + 1))"
+ echo "PLUGIN_NAME=test"
+ echo "PLUGIN_VERSION=1"
+ echo "PLUGIN_EXECUTABLES=/bin/sh"
+ ) > $mnt_dir/$plugin_meta_path
+ (
+ cd $mnt_dir;
+ find -mindepth 1 | cpio -o -Hnewc 2>/dev/null
+ ) | gzip -c > $mnt_dir/test.$plugin_ext
+
+ do_scan | grep -q 'No plugins'
+}
+
test_empty_scan()
{
__pb_mount_dir="$test_tmpdir/mnt"
do_test is_url "git+ssh://example.com/path"
do_test test_http_download
do_test test_ftp_download
+ do_test ! test_abi_check
+ do_test ! test_abi_check 1
+ do_test test_abi_check 1 1
+ do_test test_abi_check 1 1 1
+ do_test test_abi_check 1 2 0
+ do_test test_abi_check 1 2 1
+ do_test ! test_abi_check 1 2 2
do_test test_scan
do_test test_scan_nogzip
do_test test_scan_nocpio
do_test test_scan_multiple
+ do_test test_scan_wrongabi
do_test test_empty_scan
if [ $test_failed = 0 ]
shift
do_create $@
;;
+lint)
+ shift
+ do_lint $@
+ ;;
__wrap)
shift
do_wrap $@
exit 1
;;
*)
- echo "Invalid command: $s" >&2
+ echo "Invalid command: $1" >&2
usage
exit 1
esac