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 - 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
159 echo "error: install requires a file/URL argument." >&2
168 trap "rm '$file'" EXIT
169 download "$url" "$file"
172 echo "error: failed to download $url" >&2
179 echo "error: $file doesn't exist or is not readable" >&2
184 echo "File '$name' has the following sha256 checksum:"
186 sha256sum "$file" | cut -f1 -d' '
189 echo "Do you want to install this plugin? (y/N)"
202 gunzip -c "$file" | ( cd $__dest && cpio -i -d)
206 echo "error: Failed to extract archive $url, exiting"
211 parse_meta $__dest/$plugin_meta_path
213 if ! plugin_abi_check
215 echo "Plugin at $url is incompatible with this firmware," \
221 for binary in ${PLUGIN_EXECUTABLES}
223 __create_wrapper "$__dest" "$binary"
226 echo "Plugin installed"
232 local mnt dev plugin_path __meta_tmp
236 for plugin_path in $mnt/*.$plugin_ext
238 [ -e "$plugin_path" ] || continue
240 # extract plugin metadata to a temporary directory
241 __meta_tmp=$(mktemp -d)
242 [ -d $__meta_tmp ] || continue
243 gunzip -c "$plugin_path" 2>/dev/null |
245 cpio -i -d $plugin_meta_path 2>/dev/null)
246 if ! [ $? = 0 -a -e "$plugin_path" ]
253 parse_meta $__meta_tmp/$plugin_meta_path
255 plugin_abi_check || exit 1
257 printf "Plugin found on %s:\n" $dev
260 printf "To run this plugin:\n"
261 printf " $0 install $plugin_path\n"
276 for mnt in $__pb_mount_dir/*
283 echo "No plugins found"
289 local vendorname vendorshortname
290 local pluginname pluginnhortname
291 local version date executable
298 Enter the vendor company / author name. This can contain spaces.
299 (eg. 'Example Corporation')
304 Enter the vendor shortname. This should be a single-word abbreviation, in all
305 lower-case. This is only used in internal filenames.
307 Typically, a stock-ticker name is used for this (eg 'exco')
313 Enter the descriptive plugin name. This can contain spaces, but should only be
314 a few words in length (eg 'RAID configuration utility')
320 Enter the plugin shortname. This should not contain spaces, but hyphens are
321 fine (eg 'raid-config'). This is only used in internal filnames.
328 Enter the plugin version. This should not contain spaces (eg 1.2):
334 Enter the full path (within the plugin root) to the plugin executable file(s).
335 These will be exposed as wrapper scripts, to be run from the standard petitboot
336 shell environment (eg, /usr/bin/my-raid-config).
338 If multiple executables are provided, separate with a space.
342 date=$(date +%Y-%m-%d)
344 mkdir -p $(dirname $file)
347 PLUGIN_ABI='$plugin_abi'
349 PLUGIN_VENDOR='$vendorname'
350 PLUGIN_VENDOR_ID='$vendorshortname'
351 PLUGIN_NAME='$pluginname'
352 PLUGIN_ID='$pluginshortname'
353 PLUGIN_VERSION='$version'
355 PLUGIN_EXECUTABLES='$executables'
362 local src meta_dir_abs meta_file
367 echo "error: missing source directory" >&2
374 echo "error: source directory missing" >&2
378 meta_file=$src/$plugin_meta_path
380 if [ ! -e $meta_file ]
382 echo "No plugin metadata file found. " \
383 "Would you like to create one? (Y/n)"
387 echo "Cancelled, exiting"
391 guided_meta $meta_file || exit
394 # Sanity check metadata file
395 parse_meta $meta_file
407 outfile=${PLUGIN_ID}-${PLUGIN_VERSION}.${plugin_ext}
411 find -mindepth 1 | cpio -o -Hnewc -v
412 ) | gzip -c > $outfile
415 echo "Plugin metadata:"
416 sed -e 's/^/ /' $meta_file
419 echo "User-visible metadata:"
420 plugin_info | sed -e 's/^/ /'
428 Ship this file in the top-level-directory of a USB device or CD to have it
429 automatically discoverable by 'pb-plugin scan'. This file can be re-named,
430 but must retain the .$plugin_ext extension to be discoverable.
437 [ -d "$__dest" ] && rm -rf "$__dest"
444 errors=$(($errors+1))
450 warnings=$(($warnings+1))
455 [ -n "$PLUGIN_ABI" ] ||
456 lint_err "no PLUGIN_ABI defined in metadata"
458 printf '%s' "$PLUGIN_ABI" | grep -q '[^0-9]' &&
459 lint_err "PLUGIN_ABI has non-numeric characters"
461 [ -n "$PLUGIN_ABI_MIN" ] ||
462 lint_err "no PLUGIN_ABI_MIN defined in metadata"
464 printf '%s' "$PLUGIN_ABI_MIN" | grep -q '[^0-9]' &&
465 lint_err "PLUGIN_ABI_MIN has non-numeric characters"
467 [ "$PLUGIN_ABI" = "$plugin_abi" ] ||
468 lint_warn "PLUGIN_ABI (=$PLUGIN_ABI) is not $plugin_abi"
470 [ -n "$PLUGIN_VENDOR" ] ||
471 lint_err "no PLUGIN_VENDOR defined in metadata"
473 [ -n "$PLUGIN_VENDOR_ID" ] ||
474 lint_err "no PLUGIN_VENDOR_ID defined in metadata"
476 printf '%s' "$PLUGIN_VENDOR_ID" | grep -q '[^a-z0-9-]' &&
477 lint_err "PLUGIN_VENDOR_ID should only contain lowercase" \
478 "alphanumerics and hyphens"
480 [ -n "$PLUGIN_NAME" ] ||
481 lint_err "no PLUGIN_NAME defined in metadata"
483 [ -n "$PLUGIN_ID" ] ||
484 lint_err "no PLUGIN_ID defined in metadata"
486 printf '%s' "$PLUGIN_ID" | grep -q '[^a-z0-9-]' &&
487 lint_err "PLUGIN_ID should only contain lowercase" \
488 "alphanumerics and hyphens"
490 [ "$PLUGIN_VERSION" ] ||
491 lint_err "no PLUGIN_VERSION defined in metadata"
493 [ -n "$PLUGIN_DATE" ] ||
494 lint_err "no PLUGIN_DATE defined in metadata"
496 [ -n "$PLUGIN_EXECUTABLES" ] ||
497 lint_err "no PLUGIN_EXECUTABLES defined in metadata"
502 local plugin_file errors warnings __dest executable dir
509 [ "${plugin_file##*.}" = $plugin_ext ] ||
510 lint_err "Plugin file does not end with $plugin_ext"
512 gunzip -c "$plugin_file" > /dev/null 2>&1 ||
513 lint_fatal "Plugin can't be gunzipped"
515 gunzip -c "$plugin_file" 2>/dev/null | cpio -t >/dev/null 2>&1 ||
516 lint_fatal "Plugin can't be cpioed"
519 gunzip -c "$plugin_file" | ( cd $__dest && cpio -i -d 2>/dev/null)
521 [ -e "$__dest/$plugin_meta_path" ] ||
522 lint_fatal "No metadata file present (expecting" \
525 parse_meta "$__dest/$plugin_meta_path"
528 for executable in ${PLUGIN_EXECUTABLES}
530 exec_path="$__dest/$executable"
531 [ -e "$exec_path" ] || {
532 lint_err "PLUGIN_EXECUTABLES item $executable" \
537 [ -x "$exec_path" ] ||
538 lint_err "PLUGIN_EXECUTABLES item $executable" \
542 for dir in dev sys proc var
544 [ -e "$__dest/$dir" ] || continue
546 [ -d "$__dest/$dir" ] ||
547 lint_err "/$dir exists, but isn't a directory"
549 [ "$(find $__dest/$dir -mindepth 1)" ] &&
550 lint_warn "/$dir contains files/directories," \
551 "these will be lost during chroot setup"
554 printf '%s: %d errors, %d warnings\n' $plugin_file $errors $warnings
563 tmp=$(mktemp -p $test_tmpdir)
564 ref=$(mktemp -p $test_tmpdir)
573 download http://example.com/test $tmp
581 tmp=$(mktemp -p $test_tmpdir)
582 ref=$(mktemp -p $test_tmpdir)
591 download ftp://example.com/test $tmp
607 __pb_mount_dir="$test_tmpdir/mnt"
608 mnt_dir="$__pb_mount_dir/sda"
609 mkdir -p $mnt_dir/$plugin_meta_dir
611 echo "PLUGIN_ABI=$plugin_abi"
612 echo "PLUGIN_NAME=test"
613 echo "PLUGIN_VERSION=1"
614 echo "PLUGIN_EXECUTABLES=/bin/sh"
615 ) > $mnt_dir/$plugin_meta_path
618 find -mindepth 1 | cpio -o -Hnewc 2>/dev/null
619 ) | gzip -c > $mnt_dir/test.$plugin_ext
621 do_scan | grep -q 'test'
626 __pb_mount_dir="$test_tmpdir/mnt"
627 mnt_dir="$__pb_mount_dir/sda"
628 stderr_file="$test_tmpdir/stderr"
631 echo "invalid" > $mnt_dir/nogzip.$plugin_ext
633 do_scan 2>$stderr_file | grep -q 'No plugins'
635 [ $? = 0 ] || return 1
637 if [ -s "$stderr_file" ]
639 echo "Scan with invalid (non-gzip) file produced error output" \
649 __pb_mount_dir="$test_tmpdir/mnt"
650 mnt_dir="$__pb_mount_dir/sda"
651 stderr_file="$test_tmpdir/stderr"
654 echo "invalid" | gzip -c > $mnt_dir/nogzip.$plugin_ext
656 do_scan 2>$stderr_file | grep -q 'No plugins'
658 [ $? = 0 ] || return 1
660 if [ -s "$stderr_file" ]
662 echo "Scan with invalid (non-cpio) file produced error output" \
672 __pb_mount_dir="$test_tmpdir/mnt"
673 mnt_dir="$__pb_mount_dir/sda"
674 outfile=$test_tmpdir/scan.out
678 mkdir -p $mnt_dir/$plugin_meta_dir
680 echo "PLUGIN_ABI=$plugin_abi"
681 echo "PLUGIN_NAME=test-$i"
682 echo "PLUGIN_VERSION=1"
683 echo "PLUGIN_EXECUTABLES=/bin/sh"
684 ) > $mnt_dir/$plugin_meta_path
687 find -mindepth 1 | cpio -o -Hnewc 2>/dev/null
688 ) | gzip -c > $mnt_dir/test-${i}.$plugin_ext
689 rm -rf $mnt_dir/$plugin_meta_dir
694 grep -q 'test-1' $outfile && grep -q 'test-2' $outfile
699 __pb_mount_dir="$test_tmpdir/mnt"
700 mnt_dir="$__pb_mount_dir/sda"
701 mkdir -p $mnt_dir/$plugin_meta_dir
703 echo "PLUGIN_ABI=$(($plugin_abi + 1))"
704 echo "PLUGIN_ABI_MIN=$(($plugin_abi + 1))"
705 echo "PLUGIN_NAME=test"
706 echo "PLUGIN_VERSION=1"
707 echo "PLUGIN_EXECUTABLES=/bin/sh"
708 ) > $mnt_dir/$plugin_meta_path
711 find -mindepth 1 | cpio -o -Hnewc 2>/dev/null
712 ) | gzip -c > $mnt_dir/test.$plugin_ext
714 do_scan | grep -q 'No plugins'
719 __pb_mount_dir="$test_tmpdir/mnt"
720 mkdir -p $__pb_mount_dir
721 do_scan | grep -q "No plugins"
728 test_tmpdir="$tests_tmpdir/$n"
770 tests_tmpdir=$(mktemp -d)
773 do_test ! is_url "/test"
774 do_test ! is_url "./test"
775 do_test ! is_url "../test"
776 do_test ! is_url "test"
777 do_test is_url "http://example.com/path"
778 do_test is_url "git+ssh://example.com/path"
779 do_test test_http_download
780 do_test test_ftp_download
781 do_test ! test_abi_check
782 do_test ! test_abi_check 1
783 do_test test_abi_check 1 1
784 do_test test_abi_check 1 1 1
785 do_test test_abi_check 1 2 0
786 do_test test_abi_check 1 2 1
787 do_test ! test_abi_check 1 2 2
789 do_test test_scan_nogzip
790 do_test test_scan_nocpio
791 do_test test_scan_multiple
792 do_test test_scan_wrongabi
793 do_test test_empty_scan
795 if [ $test_failed = 0 ]
797 echo "$n tests passed"
801 rm -rf "$tests_tmpdir"
832 echo "error: Missing command" >&2
837 echo "Invalid command: $1" >&2