#!/bin/sh __dest=/ __pb_mount_dir=/var/petitboot/mnt/dev/ __plugin_basedir=/tmp/ plugin_file=pb-plugin.cpio.gz plugin_meta=pb-plugin.conf plugin_meta_dir=etc/preboot-plugins/ plugin_meta_path=$plugin_meta_dir$plugin_meta plugin_wrapper_dir=/var/lib/pb-plugins/bin usage() { cat < Where is one of: install - install plugin from FILE/URL scan - look for available plugins on attached devices create - create a new plugin archive from DIR EOF } is_url() { local url tmp url=$1 tmp=${url#*://} [ "$url" != "$tmp" ] } download() { local url file proto url=$1 file=$2 proto=${url%://*} case "$proto" in http) wget -O - "$url" > $file ;; ftp) ncftpget -c "$url" > $file ;; *) echo "error: Unsuported protocol $proto" >&2 false esac } plugin_info() { local title if [ "$PLUGIN_VENDOR" ] then title="$PLUGIN_VENDOR: $PLUGIN_NAME" else title="$PLUGIN_NAME" fi echo "$title" echo " (version $PLUGIN_VERSION)" } do_wrap() { local base binary dir base=$1 binary=$2 shift 2 for dir in etc dev sys proc var do [ -e "$base/$dir" ] || mkdir -p "$base/$dir" done cp /etc/resolv.conf $base/etc mount -o bind /dev $base/dev mount -o bind /sys $base/sys mount -o bind /proc $base/proc mount -o bind /var $base/var chroot "$base" "$binary" "$@" umount $base/dev umount $base/sys umount $base/proc umount $base/var } __create_wrapper() { local base binary wrapper base=$1 binary=$2 wrapper=$plugin_wrapper_dir/$(basename $binary) mkdir -p $plugin_wrapper_dir cat < $wrapper #!/bin/sh exec $(realpath $0) __wrap '$base' '$binary' "\$@" EOF chmod a+x $wrapper } do_install() { local url name file __dest url=$1 if [ -z "$url" ] then echo "error: install requires a file/URL argument." >&2 exit 1 fi name=${url##*/} if is_url "$url" then file=$(mktemp) trap "rm '$file'" EXIT download "$url" "$file" if [ $? -ne 0 ] then echo "error: failed to download $url" >&2 exit 1 fi else file=$url if [ ! -r "$file" ] then echo "error: $file doesn't exist or is not readable" >&2 exit 1 fi fi echo "File '$name' has the following sha256 checksum:" 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 __dest=$(mktemp -d) gunzip -c "$file" | ( cd $__dest && cpio -i -d) if [ $? -ne 0 ] then echo "error: Failed to extract archive $url, exiting" rm -rf $__dest exit 1 fi . $__dest/$plugin_meta_path for binary in ${PLUGIN_EXECUTABLES} do __create_wrapper "$__dest" "$binary" done echo "Plugin installed" plugin_info } do_scan() { local found dev plugin_path __meta_tmp found=0 for mnt in $__pb_mount_dir/* do dev=$(basename $mnt) plugin_path="$mnt/$plugin_file" [ -e "$plugin_path" ] || continue # extract plugin metadata to a temporary directory __meta_tmp=$(mktemp -d) [ -d $__meta_tmp ] || continue gunzip -c "$plugin_path" | (cd $__meta_tmp && cpio -i -d $plugin_meta_path 2>/dev/null) if ! [ $? = 0 -a -e "$plugin_path" ] then rm -rf $__meta_tmp continue fi ( . $__meta_tmp/$plugin_meta_path printf "Plugin found on %s:\n" $dev plugin_info printf "\n" printf "To run this plugin:\n" printf " $0 run $plugin_path\n" printf "\n" ) rm -rf $__meta_tmp found=1 done if [ "$found" = 0 ] then echo "No plugins found" fi } guided_meta() { local vendorname vendorshortname local pluginname pluginnhortname local version date executable local file file=$1 cat < $file PLUGIN_VENDOR='$vendorname' PLUGIN_NAME='$pluginname' PLUGIN_VERSION='$version' PLUGIN_DATE='$date' PLUGIN_EXECUTABLES='$executables' EOF } do_create() { local src meta_dir_abs meta_file src=$1 if [ -z "$src" ] then echo "error: missing source directory" >&2 usage exit 1 fi if [ ! -d "$src" ] then echo "error: source directory missing" >&2 exit 1 fi meta_file=$src/$plugin_meta_path if [ ! -e $meta_file ] then echo "No plugin metadata file found. " \ "Would you like to create one? (Y/n)" read resp case "$resp" in [nN]|[nN][oO]) echo "Cancelled, exiting" exit 1 ;; esac guided_meta $meta_file || exit 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_NAME" ] then echo "error: no PLUGIN_NAME 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" ] then echo "error: no PLUGIN_EXECUTABLES defined in metadata"\ &>2 exit 1 fi ) || exit 1 outfile=$plugin_file ( cd $src find -mindepth 1 | cpio -o -Hnewc -v ) | gzip -c > $outfile echo echo "Plugin metadata:" sed -e 's/^/ /' $meta_file echo echo "User-visible metadata:" ( . $meta_file plugin_info | sed -e 's/^/ /' ) echo cat < $ref wget() { cat $ref } download http://example.com/test $tmp cmp -s "$ref" "$tmp" } test_ftp_download() { local tmp ref tmp=$(mktemp -p $test_tmpdir) ref=$(mktemp -p $test_tmpdir) echo $RANDOM > $ref ncftpget() { cat $ref } download ftp://example.com/test $tmp cmp -s "$ref" "$tmp" } test_scan() { __pb_mount_dir="$test_tmpdir/mnt" mnt_dir="$__pb_mount_dir/sda" mkdir -p $mnt_dir/$plugin_meta_dir ( 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/$plugin_file do_scan | grep -q 'test 1' rc=$? } test_empty_scan() { __pb_mount_dir="$test_tmpdir/mnt" mkdir -p $__pb_mount_dir do_scan | grep -q "No plugins" } test_setup() { n=$(($n+1)) test_tmpdir="$tests_tmpdir/$n" mkdir "$test_tmpdir" } test_teardown() { true } test_failed=0 do_test() { local tstr op tstr="$@" op=-eq if [ "x$1" = "x!" ] then op=-ne shift fi test_setup ( $@ ) local rc=$? test_teardown if [ $rc $op 0 ] then echo PASS: "$tstr" else echo FAIL: "$tstr" test_failed=1 false fi } do_tests() { local tests_tmpdir n tests_tmpdir=$(mktemp -d) n=0 do_test ! is_url "/test" do_test ! is_url "./test" do_test ! is_url "../test" do_test ! is_url "test" do_test is_url "http://example.com/path" do_test is_url "git+ssh://example.com/path" do_test test_http_download do_test test_ftp_download do_test test_scan do_test test_empty_scan if [ $test_failed = 0 ] then echo "$n tests passed" else echo "Tests failed" false fi rm -rf "$tests_tmpdir" } case "$1" in install) shift do_install $@ ;; scan) shift do_scan $@ ;; create) shift do_create $@ ;; __wrap) shift do_wrap $@ ;; __test) shift do_tests $@ ;; "") echo "error: Missing command" >&2 usage exit 1 ;; *) echo "Invalid command: $s" >&2 usage exit 1 esac