4 __pb_mount_dir=/var/petitboot/mnt/dev/
7 plugin_meta=pb-plugin.conf
8 plugin_meta_dir=etc/preboot-plugins/
9 plugin_meta_path=$plugin_meta_dir$plugin_meta
10 plugin_wrapper_dir=/var/lib/pb-plugins/bin
17 Where <command> is one of:
18 install <FILE|URL> - install plugin from FILE/URL
19 scan <DIR> - look for available plugins on attached devices
20 create <DIR> - create a new plugin archive from DIR
21 lint <FILE> - perform a pre-distribution check on FILE
42 wget -O - "$url" > $file
45 ncftpget -c "$url" > $file
48 echo "error: Unsuported protocol $proto" >&2
56 if [ "$PLUGIN_VENDOR" ]
58 title="$PLUGIN_VENDOR: $PLUGIN_NAME"
64 echo " (version $PLUGIN_VERSION)"
69 local file name value IFS
74 while read -r name value
76 # Ensure we have a sensible variable name
77 echo "$name" | grep -q '^PLUGIN_[A-Z_]*$' || continue
79 # we know that $name has no quoting/expansion chars, but we
80 # may need to do some basic surrounding-quote removal for
81 # $value, without evaluating it
82 value=$(echo "$value" | sed "s/^\([\"']\)\(.*\)\1\$/\2/g")
88 # How the ABI versioning works:
90 # - This script has an ABI defined ($plugin_abi)
92 # - Plugins have a current ABI number ($PLUGIN_ABI), and a minimum supported
93 # ABI number ($PLUGIN_ABI_MIN).
95 # - A plugin is OK to run if:
96 # - the plugin's ABI matches the script ABI, or
97 # - the plugin's minimum ABI is lower than or equal to the script ABI
100 [ -n "$PLUGIN_ABI" ] &&
101 ( [ $PLUGIN_ABI -eq $plugin_abi ] ||
102 [ $PLUGIN_ABI_MIN -le $plugin_abi ] )
107 local base binary dir
113 for dir in etc dev sys proc var
115 [ -e "$base/$dir" ] || mkdir -p "$base/$dir"
118 cp /etc/resolv.conf $base/etc
119 mount -o bind /dev $base/dev
120 mount -o bind /sys $base/sys
121 mount -o bind /proc $base/proc
122 mount -o bind /var $base/var
124 chroot "$base" "$binary" "$@"
134 local base binary wrapper
138 wrapper=$plugin_wrapper_dir/$(basename $binary)
140 mkdir -p $plugin_wrapper_dir
145 exec $(realpath $0) __wrap '$base' '$binary' "\$@"
153 local url name file __dest auto
155 if [ "$1" == "auto" ]; then
164 echo "error: install requires a file/URL argument." >&2
173 trap "rm '$file'" EXIT
174 download "$url" "$file"
177 echo "error: failed to download $url" >&2
184 echo "error: $file doesn't exist or is not readable" >&2
189 echo "File '$name' has the following sha256 checksum:"
191 sha256sum "$file" | cut -f1 -d' '
196 echo "Do you want to install this plugin? (y/N)"
210 gunzip -c "$file" | ( cd $__dest && cpio -i -d)
214 echo "error: Failed to extract archive $url, exiting"
219 parse_meta $__dest/$plugin_meta_path
221 if ! plugin_abi_check
223 echo "Plugin at $url is incompatible with this firmware," \
229 for binary in ${PLUGIN_EXECUTABLES}
231 __create_wrapper "$__dest" "$binary"
234 pb-event plugin@local \
235 name="$PLUGIN_NAME" id=$PLUGIN_ID version=$PLUGIN_VERSION \
236 vendor="$PLUGIN_VENDOR" vendor_id=$PLUGIN_VENDOR_ID \
237 date=$PLUGIN_DATE executables="$PLUGIN_EXECUTABLES" \
238 source_file=$url installed="yes"
240 echo "Plugin installed"
246 local mnt dev plugin_path __meta_tmp
250 for plugin_path in $mnt/*.$plugin_ext
252 [ -e "$plugin_path" ] || continue
254 # extract plugin metadata to a temporary directory
255 __meta_tmp=$(mktemp -d)
256 [ -d $__meta_tmp ] || continue
257 gunzip -c "$plugin_path" 2>/dev/null |
259 cpio -i -d $plugin_meta_path 2>/dev/null)
260 if ! [ $? = 0 -a -e "$plugin_path" ]
267 parse_meta $__meta_tmp/$plugin_meta_path
269 plugin_abi_check || exit 1
271 printf "Plugin found on %s:\n" $dev
274 printf "To run this plugin:\n"
275 printf " $0 install $plugin_path\n"
278 pb-event plugin@$dev name=$PLUGIN_NAME \
279 path=$plugin_path installed="no"
291 local found mnt dev locations
296 locations=$__pb_mount_dir/*
298 echo "Scanning device $dev"
302 for mnt in $locations
309 echo "No plugins found"
315 local vendorname vendorshortname
316 local pluginname pluginnhortname
317 local version date executable
324 Enter the vendor company / author name. This can contain spaces.
325 (eg. 'Example Corporation')
330 Enter the vendor shortname. This should be a single-word abbreviation, in all
331 lower-case. This is only used in internal filenames.
333 Typically, a stock-ticker name is used for this (eg 'exco')
339 Enter the descriptive plugin name. This can contain spaces, but should only be
340 a few words in length (eg 'RAID configuration utility')
346 Enter the plugin shortname. This should not contain spaces, but hyphens are
347 fine (eg 'raid-config'). This is only used in internal filnames.
354 Enter the plugin version. This should not contain spaces (eg 1.2):
360 Enter the full path (within the plugin root) to the plugin executable file(s).
361 These will be exposed as wrapper scripts, to be run from the standard petitboot
362 shell environment (eg, /usr/bin/my-raid-config).
364 If multiple executables are provided, separate with a space.
368 date=$(date +%Y-%m-%d)
370 mkdir -p $(dirname $file)
373 PLUGIN_ABI='$plugin_abi'
375 PLUGIN_VENDOR='$vendorname'
376 PLUGIN_VENDOR_ID='$vendorshortname'
377 PLUGIN_NAME='$pluginname'
378 PLUGIN_ID='$pluginshortname'
379 PLUGIN_VERSION='$version'
381 PLUGIN_EXECUTABLES='$executables'
388 local src meta_dir_abs meta_file
393 echo "error: missing source directory" >&2
400 echo "error: source directory missing" >&2
404 meta_file=$src/$plugin_meta_path
406 if [ ! -e $meta_file ]
408 echo "No plugin metadata file found. " \
409 "Would you like to create one? (Y/n)"
413 echo "Cancelled, exiting"
417 guided_meta $meta_file || exit
420 # Sanity check metadata file
421 parse_meta $meta_file
433 outfile=${PLUGIN_ID}-${PLUGIN_VERSION}.${plugin_ext}
437 find -mindepth 1 | cpio -o -Hnewc -v
438 ) | gzip -c > $outfile
441 echo "Plugin metadata:"
442 sed -e 's/^/ /' $meta_file
445 echo "User-visible metadata:"
446 plugin_info | sed -e 's/^/ /'
454 Ship this file in the top-level-directory of a USB device or CD to have it
455 automatically discoverable by 'pb-plugin scan'. This file can be re-named,
456 but must retain the .$plugin_ext extension to be discoverable.
463 [ -d "$__dest" ] && rm -rf "$__dest"
470 errors=$(($errors+1))
476 warnings=$(($warnings+1))
481 [ -n "$PLUGIN_ABI" ] ||
482 lint_err "no PLUGIN_ABI defined in metadata"
484 printf '%s' "$PLUGIN_ABI" | grep -q '[^0-9]' &&
485 lint_err "PLUGIN_ABI has non-numeric characters"
487 [ -n "$PLUGIN_ABI_MIN" ] ||
488 lint_err "no PLUGIN_ABI_MIN defined in metadata"
490 printf '%s' "$PLUGIN_ABI_MIN" | grep -q '[^0-9]' &&
491 lint_err "PLUGIN_ABI_MIN has non-numeric characters"
493 [ "$PLUGIN_ABI" = "$plugin_abi" ] ||
494 lint_warn "PLUGIN_ABI (=$PLUGIN_ABI) is not $plugin_abi"
496 [ -n "$PLUGIN_VENDOR" ] ||
497 lint_err "no PLUGIN_VENDOR defined in metadata"
499 [ -n "$PLUGIN_VENDOR_ID" ] ||
500 lint_err "no PLUGIN_VENDOR_ID defined in metadata"
502 printf '%s' "$PLUGIN_VENDOR_ID" | grep -q '[^a-z0-9-]' &&
503 lint_err "PLUGIN_VENDOR_ID should only contain lowercase" \
504 "alphanumerics and hyphens"
506 [ -n "$PLUGIN_NAME" ] ||
507 lint_err "no PLUGIN_NAME defined in metadata"
509 [ -n "$PLUGIN_ID" ] ||
510 lint_err "no PLUGIN_ID defined in metadata"
512 printf '%s' "$PLUGIN_ID" | grep -q '[^a-z0-9-]' &&
513 lint_err "PLUGIN_ID should only contain lowercase" \
514 "alphanumerics and hyphens"
516 [ "$PLUGIN_VERSION" ] ||
517 lint_err "no PLUGIN_VERSION defined in metadata"
519 [ -n "$PLUGIN_DATE" ] ||
520 lint_err "no PLUGIN_DATE defined in metadata"
522 [ -n "$PLUGIN_EXECUTABLES" ] ||
523 lint_err "no PLUGIN_EXECUTABLES defined in metadata"
528 local plugin_file errors warnings __dest executable dir
535 [ "${plugin_file##*.}" = $plugin_ext ] ||
536 lint_err "Plugin file does not end with $plugin_ext"
538 gunzip -c "$plugin_file" > /dev/null 2>&1 ||
539 lint_fatal "Plugin can't be gunzipped"
541 gunzip -c "$plugin_file" 2>/dev/null | cpio -t >/dev/null 2>&1 ||
542 lint_fatal "Plugin can't be cpioed"
545 gunzip -c "$plugin_file" | ( cd $__dest && cpio -i -d 2>/dev/null)
547 [ -e "$__dest/$plugin_meta_path" ] ||
548 lint_fatal "No metadata file present (expecting" \
551 parse_meta "$__dest/$plugin_meta_path"
554 for executable in ${PLUGIN_EXECUTABLES}
556 exec_path="$__dest/$executable"
557 [ -e "$exec_path" ] || {
558 lint_err "PLUGIN_EXECUTABLES item $executable" \
563 [ -x "$exec_path" ] ||
564 lint_err "PLUGIN_EXECUTABLES item $executable" \
568 for dir in dev sys proc var
570 [ -e "$__dest/$dir" ] || continue
572 [ -d "$__dest/$dir" ] ||
573 lint_err "/$dir exists, but isn't a directory"
575 [ "$(find $__dest/$dir -mindepth 1)" ] &&
576 lint_warn "/$dir contains files/directories," \
577 "these will be lost during chroot setup"
580 printf '%s: %d errors, %d warnings\n' $plugin_file $errors $warnings
589 tmp=$(mktemp -p $test_tmpdir)
590 ref=$(mktemp -p $test_tmpdir)
599 download http://example.com/test $tmp
607 tmp=$(mktemp -p $test_tmpdir)
608 ref=$(mktemp -p $test_tmpdir)
617 download ftp://example.com/test $tmp
633 __pb_mount_dir="$test_tmpdir/mnt"
634 mnt_dir="$__pb_mount_dir/sda"
635 mkdir -p $mnt_dir/$plugin_meta_dir
637 echo "PLUGIN_ABI=$plugin_abi"
638 echo "PLUGIN_NAME=test"
639 echo "PLUGIN_VERSION=1"
640 echo "PLUGIN_EXECUTABLES=/bin/sh"
641 ) > $mnt_dir/$plugin_meta_path
644 find -mindepth 1 | cpio -o -Hnewc 2>/dev/null
645 ) | gzip -c > $mnt_dir/test.$plugin_ext
647 do_scan | grep -q 'test'
652 __pb_mount_dir="$test_tmpdir/mnt"
653 mnt_dir="$__pb_mount_dir/sda"
654 stderr_file="$test_tmpdir/stderr"
657 echo "invalid" > $mnt_dir/nogzip.$plugin_ext
659 do_scan 2>$stderr_file | grep -q 'No plugins'
661 [ $? = 0 ] || return 1
663 if [ -s "$stderr_file" ]
665 echo "Scan with invalid (non-gzip) file produced error output" \
675 __pb_mount_dir="$test_tmpdir/mnt"
676 mnt_dir="$__pb_mount_dir/sda"
677 stderr_file="$test_tmpdir/stderr"
680 echo "invalid" | gzip -c > $mnt_dir/nogzip.$plugin_ext
682 do_scan 2>$stderr_file | grep -q 'No plugins'
684 [ $? = 0 ] || return 1
686 if [ -s "$stderr_file" ]
688 echo "Scan with invalid (non-cpio) file produced error output" \
698 __pb_mount_dir="$test_tmpdir/mnt"
699 mnt_dir="$__pb_mount_dir/sda"
700 outfile=$test_tmpdir/scan.out
704 mkdir -p $mnt_dir/$plugin_meta_dir
706 echo "PLUGIN_ABI=$plugin_abi"
707 echo "PLUGIN_NAME=test-$i"
708 echo "PLUGIN_VERSION=1"
709 echo "PLUGIN_EXECUTABLES=/bin/sh"
710 ) > $mnt_dir/$plugin_meta_path
713 find -mindepth 1 | cpio -o -Hnewc 2>/dev/null
714 ) | gzip -c > $mnt_dir/test-${i}.$plugin_ext
715 rm -rf $mnt_dir/$plugin_meta_dir
720 grep -q 'test-1' $outfile && grep -q 'test-2' $outfile
725 __pb_mount_dir="$test_tmpdir/mnt"
726 mnt_dir="$__pb_mount_dir/sda"
727 mkdir -p $mnt_dir/$plugin_meta_dir
729 echo "PLUGIN_ABI=$(($plugin_abi + 1))"
730 echo "PLUGIN_ABI_MIN=$(($plugin_abi + 1))"
731 echo "PLUGIN_NAME=test"
732 echo "PLUGIN_VERSION=1"
733 echo "PLUGIN_EXECUTABLES=/bin/sh"
734 ) > $mnt_dir/$plugin_meta_path
737 find -mindepth 1 | cpio -o -Hnewc 2>/dev/null
738 ) | gzip -c > $mnt_dir/test.$plugin_ext
740 do_scan | grep -q 'No plugins'
745 __pb_mount_dir="$test_tmpdir/mnt"
746 mkdir -p $__pb_mount_dir
747 do_scan | grep -q "No plugins"
754 test_tmpdir="$tests_tmpdir/$n"
796 tests_tmpdir=$(mktemp -d)
799 do_test ! is_url "/test"
800 do_test ! is_url "./test"
801 do_test ! is_url "../test"
802 do_test ! is_url "test"
803 do_test is_url "http://example.com/path"
804 do_test is_url "git+ssh://example.com/path"
805 do_test test_http_download
806 do_test test_ftp_download
807 do_test ! test_abi_check
808 do_test ! test_abi_check 1
809 do_test test_abi_check 1 1
810 do_test test_abi_check 1 1 1
811 do_test test_abi_check 1 2 0
812 do_test test_abi_check 1 2 1
813 do_test ! test_abi_check 1 2 2
815 do_test test_scan_nogzip
816 do_test test_scan_nocpio
817 do_test test_scan_multiple
818 do_test test_scan_wrongabi
819 do_test test_empty_scan
821 if [ $test_failed = 0 ]
823 echo "$n tests passed"
827 rm -rf "$tests_tmpdir"
858 echo "error: Missing command" >&2
863 echo "Invalid command: $1" >&2