]> git.ozlabs.org Git - petitboot/blobdiff - utils/pb-plugin
pb-plugin: Only require a fixed plugin extension, instead of full name
[petitboot] / utils / pb-plugin
index e71e981a249cc1a74574600d5743be102fc1f595..c6c9ef97ccc4dd7f7e02196f03a978c48ce7bc41 100755 (executable)
@@ -2,8 +2,11 @@
 
 __dest=/
 __pb_mount_dir=/var/petitboot/mnt/dev/
-plugin_dev_meta=pb-plugin.conf
-plugin_installed_meta_dir=/etc/preboot-plugins/
+plugin_ext=pb-plugin
+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()
 {
@@ -11,10 +14,9 @@ usage()
 Usage: $0 <command>
 
 Where <command> is one of:
-  install <FILE|URL>  - install plugin from FILE/URL
-  scan                - look for available plugins on attached devices
-  list                - list currently-installed plugins
-  create <DIR>        - create a new plugin archive from DIR
+  install <FILE|URL> - install plugin from FILE/URL
+  scan               - look for available plugins on attached devices
+  create <DIR>       - create a new plugin archive from DIR
 EOF
 }
 
@@ -60,9 +62,55 @@ plugin_info()
        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 <<EOF > $wrapper
+#!/bin/sh
+
+exec $(realpath $0) __wrap '$base' '$binary' "\$@"
+EOF
+
+       chmod a+x $wrapper
+}
+
 do_install()
 {
-       local url
+       local url name file __dest
 
        url=$1
 
@@ -72,18 +120,6 @@ do_install()
                exit 1
        fi
 
-       if [ ! -d "$__dest" ]
-       then
-               echo "error: destination directory '$__dest' doesn't exist" >&2
-               exit 1
-       fi
-
-       if [ ! -w "$__dest" ]
-       then
-               echo "error: destination directory isn't writeable" >&2
-               exit 1
-       fi
-
        name=${url##*/}
 
        if is_url "$url"
@@ -109,7 +145,7 @@ do_install()
        echo
        sha256sum "$file" | cut -f1 -d' '
        echo
-       echo "Do you want to install into the pre-boot environment? (y/N)"
+       echo "Do you want to install this plugin? (y/N)"
        read resp
 
        case $resp in
@@ -121,61 +157,76 @@ do_install()
                ;;
        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()
+do_scan_mount()
 {
-       local found
-       found=0
-       for mnt in $__pb_mount_dir/*
+       local mnt dev plugin_path __meta_tmp
+       mnt=$1
+       dev=$(basename $mnt)
+
+       for plugin_path in $mnt/*.$plugin_ext
        do
-               dev=$(basename $mnt)
-               metafile="$mnt/$plugin_dev_meta"
-               [ -e "$metafile" ] || continue
+               [ -e "$plugin_path" ] || continue
+
+               # extract plugin metadata to a temporary directory
+               __meta_tmp=$(mktemp -d)
+               [ -d $__meta_tmp ] || continue
+               gunzip -c "$plugin_path" 2>/dev/null |
+                       (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
+
                (
-                       . $metafile
+                       . $__meta_tmp/$plugin_meta_path
+
                        printf "Plugin found on %s:\n" $dev
                        plugin_info
                        printf "\n"
-                       printf "To install this plugin, run:\n"
-                       printf "  $0 install $mnt/$PLUGIN_FILE\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
 }
 
-do_list()
+do_scan()
 {
-       local found
+       local found mnt
        found=0
-       for meta in $plugin_installed_meta_dir/*
+       for mnt in $__pb_mount_dir/*
        do
-               [ -e "$meta" ] || continue
-               [ $found = 0 ] && printf "Installed plugins:\n"
-               found=1
-               (
-                       . $meta
-                       plugin_info
-                       echo
-               )
+               do_scan_mount $mnt
        done
 
        if [ "$found" = 0 ]
        then
-               echo "No plugins installed"
+               echo "No plugins found"
        fi
 }
 
@@ -183,10 +234,10 @@ guided_meta()
 {
        local vendorname vendorshortname
        local pluginname pluginnhortname
-       local version date
-       local dir
+       local version date executable
+       local file
 
-       dir=$1
+       file=$1
 
 cat <<EOF
 
@@ -224,22 +275,35 @@ Enter the plugin version. This should not contain spaces (eg 1.2):
 EOF
        read version
 
+cat <<EOF
+
+Enter the full path (within the plugin root) to the plugin executable file(s).
+These will be exposed as wrapper scripts, to be run from the standard petitboot
+shell environment (eg, /usr/bin/my-raid-config).
+
+If multiple executables are provided, separate with a space.
+EOF
+       read executables
+
        date=$(date +%Y-%m-%d)
 
-       mkdir -p $dir
+       mkdir -p $(dirname $file)
 
-       cat <<EOF > $dir/$vendorshortname-$pluginshortname
+       cat <<EOF > $file
 PLUGIN_VENDOR='$vendorname'
+PLUGIN_VENDOR_ID='$vendorshortname'
 PLUGIN_NAME='$pluginname'
+PLUGIN_ID='$pluginshortname'
 PLUGIN_VERSION='$version'
 PLUGIN_DATE='$date'
+PLUGIN_EXECUTABLES='$executables'
 EOF
 
 }
 
 do_create()
 {
-       local src found meta_dir_abs meta_file
+       local src meta_dir_abs meta_file
        src=$1
 
        if [ -z "$src" ]
@@ -255,16 +319,9 @@ do_create()
                exit 1
        fi
 
-       meta_dir_abs="$src/$plugin_installed_meta_dir"
-       found=0
-       for meta in $meta_dir_abs/*
-       do
-               [ -e "$meta" ] || continue
-               found=$(($found+1))
-               meta_file=$meta
-       done
+       meta_file=$src/$plugin_meta_path
 
-       if [ $found = 0 ]
+       if [ ! -e $meta_file ]
        then
                echo "No plugin metadata file found. " \
                        "Would you like to create one? (Y/n)"
@@ -275,51 +332,53 @@ do_create()
                        exit 1
                        ;;
                esac
-               guided_meta $meta_dir_abs || exit
-               meta_file=$meta_dir_abs/*
+               guided_meta $meta_file || exit
        fi
 
-       if [ $found -gt 1 ]
+       # Sanity check metadata file
+       . $meta_file
+       if [ ! -n "$PLUGIN_VENDOR" ]
        then
-               echo "error: Multiple metadata files found in $meta_dir_abs" >&2
+               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" ]
+       then
+               echo "error: no PLUGIN_EXECUTABLES defined in metadata" &>2
                exit 1
        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
-
-       ) || exit 1
-
-       outfile=pb-plugin.cpio.gz
+       outfile=${PLUGIN_ID}-${PLUGIN_VERSION}.${plugin_ext}
 
        (
                cd $src
                find -mindepth 1 | cpio -o -Hnewc -v
-       ) | gzip -c > pb-plugin.cpio.gz
-
-       cp $meta_file $plugin_dev_meta
-       echo "PLUGIN_FILE='$outfile'" >> $plugin_dev_meta
+       ) | gzip -c > $outfile
 
        echo
        echo "Plugin metadata:"
@@ -327,24 +386,17 @@ do_create()
        echo
 
        echo "User-visible metadata:"
-
-       (
-               . $meta_file
-               plugin_info | sed -e 's/^/  /'
-       )
+       plugin_info | sed -e 's/^/  /'
 
        echo
 
-
 cat <<EOF
 Plugin created in:
   $outfile
-  
-Metadata in:
-  $plugin_dev_meta
 
-If you rename $outfile (or distribute it in a non-root directory), then
-also update the PLUGIN_FILE variable in $plugin_dev_meta.
+Ship this file in the top-level-directory of a USB device or CD to have it
+automatically discoverable by 'pb-plugin scan'. This file can be re-named,
+but must retain the .$plugin_ext extension to be discoverable.
 EOF
 }
 
@@ -388,19 +440,96 @@ test_scan()
 {
        __pb_mount_dir="$test_tmpdir/mnt"
        mnt_dir="$__pb_mount_dir/sda"
-       mkdir -p $mnt_dir
+       mkdir -p $mnt_dir/$plugin_meta_dir
        (
                echo "PLUGIN_NAME=test"
                echo "PLUGIN_VERSION=1"
-               echo "PLUGIN_FILE=data/pb-plugin.cpio.gz"
-       ) > $mnt_dir/$plugin_dev_meta
+               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 'test 1'
-       rc=$?
+       do_scan | grep -q 'test'
+}
+
+test_scan_nogzip()
+{
+       __pb_mount_dir="$test_tmpdir/mnt"
+       mnt_dir="$__pb_mount_dir/sda"
+       stderr_file="$test_tmpdir/stderr"
+
+       mkdir -p $mnt_dir
+       echo "invalid" > $mnt_dir/nogzip.$plugin_ext
+
+       do_scan 2>$stderr_file | grep -q 'No plugins'
+
+       [ $? = 0 ] || return 1
+
+       if [ -s "$stderr_file" ]
+       then
+               echo "Scan with invalid (non-gzip) file produced error output" \
+                       >&2
+               cat "$stderr_file"
+               return 1
+       fi
+       true
+}
+
+test_scan_nocpio()
+{
+       __pb_mount_dir="$test_tmpdir/mnt"
+       mnt_dir="$__pb_mount_dir/sda"
+       stderr_file="$test_tmpdir/stderr"
+
+       mkdir -p $mnt_dir
+       echo "invalid" | gzip -c > $mnt_dir/nogzip.$plugin_ext
+
+       do_scan 2>$stderr_file | grep -q 'No plugins'
+
+       [ $? = 0 ] || return 1
+
+       if [ -s "$stderr_file" ]
+       then
+               echo "Scan with invalid (non-cpio) file produced error output" \
+                       >&2
+               cat "$stderr_file"
+               return 1
+       fi
+       true
+}
+
+test_scan_multiple()
+{
+       __pb_mount_dir="$test_tmpdir/mnt"
+       mnt_dir="$__pb_mount_dir/sda"
+       outfile=$test_tmpdir/scan.out
+
+       for i in 1 2
+       do
+               mkdir -p $mnt_dir/$plugin_meta_dir
+               (
+                       echo "PLUGIN_NAME=test-$i"
+                       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-${i}.$plugin_ext
+               rm -rf $mnt_dir/$plugin_meta_dir
+       done
+
+       do_scan >$outfile
+
+       grep -q 'test-1' $outfile && grep -q 'test-2' $outfile
 }
 
 test_empty_scan()
 {
+       __pb_mount_dir="$test_tmpdir/mnt"
+       mkdir -p $__pb_mount_dir
        do_scan | grep -q "No plugins"
 }
 
@@ -410,10 +539,6 @@ test_setup()
 
        test_tmpdir="$tests_tmpdir/$n"
        mkdir "$test_tmpdir"
-       __test_dest="$test_tmpdir/base"
-       mkdir "$__test_dest"
-       [ -d "$__test_dest" ] || exit 1
-       __dest=$__test_dest
 }
 
 test_teardown()
@@ -466,6 +591,9 @@ do_tests()
        do_test test_http_download
        do_test test_ftp_download
        do_test test_scan
+       do_test test_scan_nogzip
+       do_test test_scan_nocpio
+       do_test test_scan_multiple
        do_test test_empty_scan
 
        if [ $test_failed = 0 ]
@@ -473,9 +601,10 @@ do_tests()
                echo "$n tests passed"
        else
                echo "Tests failed"
-               false
        fi
        rm -rf "$tests_tmpdir"
+
+       [ $test_failed = 0 ]
 }
 
 case "$1" in
@@ -487,14 +616,14 @@ scan)
        shift
        do_scan $@
        ;;
-list)
-       shift
-       do_list $@
-       ;;
 create)
        shift
        do_create $@
        ;;
+__wrap)
+       shift
+       do_wrap $@
+       ;;
 __test)
        shift
        do_tests $@