Jim Meyering
2008-Dec-03 12:45 UTC
[Ovirt-devel] [PATCH node-image] edit-livecd: add -o OUTPUT.iso option, required
The new -o OUTPUT.iso option is now required. Allow the '-p CODE' option to specify bourne shell code, not just a file/script. This permits a common idiom: to avoid writing a separate shell script for a simple task, and instead include a quoted multi-line snippet in the invoking script, e.g., edit-livecd ...other-options... -p ' d=etc/foo mkdir -p $d && touch $d/file ' Also, in the boot params substitution, - use @ rather than / in the sed - warn if the selected string contains @ Update -h (help) output, and add examples. Evaluate $CODE in a subshell (safer and doesn't rely on pushd,popd). Guard against uninitialized ID_FS_LABEL. Don't use "function" keyword. Fail if there are any extra command-line arguments. Ensure that $PWD and $CD are double-eval safe (else fail), so that we don't have to quote them everywhere. Ensure that a $CODE (renamed from $PROG) definition doesn't leak in from the environment. --- edit-livecd | 117 ++++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 75 insertions(+), 42 deletions(-) diff --git a/edit-livecd b/edit-livecd index ce9e65b..6d6498a 100755 --- a/edit-livecd +++ b/edit-livecd @@ -34,38 +34,49 @@ usage() { Usage: $ME -i LiveCD.iso [-b bootparams] [-p program] -b BOOTPARAMS optional parameters appended to the kernel command line -i LIVECD.iso LiveCD ISO to edit (default: $NODEIMG_DEFAULT) - -p PROGRAM Arbitrary program/script that is run inside of the livecd root - filesystem. This script is not run in a chroot environment so - it can access the host filesystem. The program should be executable - If program is omitted the script will pause and allow the user in - another terminal to edit the filesystem manually. After enter is - pushed the script will re-package up the ISO. + -o OUTPUT.iso specify the output file (required) + -p CODE Arbitrary CODE that is eval'd while 'cd'd into the root of + the livecd root filesystem. Note; the code is not run in + a chroot environment, so it can access the host filesystem. + If this option is omitted, this program pauses and allows + the user (in another terminal) to modify the filesystem + manually. Type <enter> when done, and the script + re-packages the ISO. -h display this help and exit +EXAMPLES + Example Script: #!/bin/sh touch etc/sysconfig/foo + Save as foo and make executable: + chmod a+x foo + Run this to create a file /etc/sysconfig/foo in the livecd filesystem + (note the use of "\$PWD/foo", not "./foo", since it will be run from a + different directory): + + $ME -i input.iso -o /tmp/result.iso -p "\$PWD/foo" + + or, equivalently, but without a separate script: + + $ME -i input.iso -o /tmp/result.iso -p 'touch etc/sysconfig/foo' - This will create a file /etc/sysconfig/foo in the livecd filesystem. EOF } -# FIXME: instead of requiring a PROGRAM, allow arbitrary shell code, -# so we can invoke $0 -p 'touch etc/sysconfig/foo' ... -# This means that if you *do* have a program in the current directory -# running "$0 -p ./my-script" won't work, since it'll be run with -# a different working dir. However, *this* will: -# $0 -p "$PWD/my-script" - # exit after any error: set -e +CODE+OUTPUT_FILE+ err=0 help=0 -while getopts :b:i:p:h c; do +while getopts :b:hi:o:p: c; do case $c in i) CD=$OPTARG;; b) PARAMS=$OPTARG;; - p) PROG=$OPTARG;; + o) OUTPUT_FILE=$OPTARG;; + p) CODE=$OPTARG;; h) help=1;; '?') err=1; warn "invalid option: \`-$OPTARG'";; :) err=1; warn "missing argument to \`-$OPTARG' option";; @@ -75,39 +86,59 @@ done test $err = 1 && { try_h; exit 1; } test $help = 1 && { usage; exit 0; } +# Require "-o OUTPUT_FILE" +test -z "$OUTPUT_FILE" \ + && { warn "no output file specified; use -o FILE.iso"; try_h; exit 1; } + +# Fail if there are any extra command-line arguments. +if test $OPTIND -le $#; then + bad_arg=$(eval "echo \$$OPTIND") + warn "extra argument '$bad_arg'"; try_h; exit 1 +fi + # first, check to see we are root if [ $( id -u ) -ne 0 ]; then die "Must run as root" fi -# FIXME: don't use which: with bash, it's a separate program -# FIXME: When failing due to a missing required program, tell the user why. -which mkisofs mksquashfs sed > /dev/null 2>&1 +# Check for some prerequisites. +# "type" prints "PROG not found" if it's not in $PATH. +type mkisofs +type mksquashfs +type sed + +sane_name() +{ + case $1 in + *[^a-zA-Z0-9._,+:/@%=-]*) false;; + *) true;; + esac +} + +# Fail if names we'll use contain white space or shell meta-characters +sane_name "$PWD" || die "invalid working directory name: $PWD" +sane_name "$CD" || die "invalid ISO name: $CD" WDIR=`mktemp -d $PWD/livecd.XXXXXXXXXX` -# FIXME: fail if $WDIR contains white space or shell meta-characters -# FIXME: do the same for $CD. - -ISO="${CD##*/}" -ISO="${ISO%.iso}-custom.iso" -function addExit() { +addExit() { EXIT="$@ ; $EXIT" trap "$EXIT" EXIT HUP TERM INT QUIT } -function mnt() { +mnt() { local margs="$1" ; shift local mp="$WDIR/$1" for D in "$@" ; do mkdir -v -p "$WDIR/$D" done - mount -v $margs "$mp" + eval mount -v $margs "$mp" addExit "df | grep $mp > /dev/null 2>&1 && umount -v $mp" } addExit "rm -rf $WDIR" +ID_FS_LABEL= # initialize, in case vol_id fails eval "$(/lib/udev/vol_id $CD)" LABEL=$ID_FS_LABEL @@ -120,21 +151,21 @@ mnt "-t squashfs $WDIR/cd/LiveOS/squashfs.img -o ro,loop" sq # create writable copy of the new filesystem for the CD cp -a $WDIR/cd $WDIR/cd-w -# create writable copy of the filesystem for the new compressed squashfs filesystem +# create writable copy of the filesystem for the new compressed +# squashfs filesystem cp -a $WDIR/sq $WDIR/sq-w # mount ext3 filesystem mnt "-t auto $WDIR/sq-w/LiveOS/ext3fs.img -o rw,loop" ex echo ">>> Updating CD content" -if [ -n "$PROG" ]; then - cp -av $PROG $WDIR/ex - pushd $WDIR/ex - set +e - ./$(basename $PROG) - set -e - popd - rm -f $WDIR/ex/$PROG +if [ -n "$CODE" ]; then + ( + cd $WDIR/ex + set +e + eval "$CODE" + set -e + ) else echo "***" echo "*** Pausing to allow manual changes. Press any key to continue." @@ -147,7 +178,7 @@ fi while :; do echo ">>> Unmounting ext3fs" umount $WDIR/ex && break - echo ">>> Unmounting ext3fs failed" + echo ">>> Unmounting the working file system copy failed" echo "***" echo "*** Did you forget to 'cd' out of $WDIR/ex?" echo "***" @@ -160,13 +191,15 @@ echo ">>> Compressing filesystem" mksquashfs $WDIR/sq-w/ $WDIR/cd-w/LiveOS/squashfs.img -noappend echo ">>> Recomputing MD5 sums" -( cd $WDIR/cd-w && find . -type f -not -name md5sum.txt -not -path '*/isolinux/*' -print0 | xargs -0 -- md5sum > md5sum.txt ) +( cd $WDIR/cd-w && find . -type f -not -name md5sum.txt \ + -not -path '*/isolinux/*' -print0 | xargs -0 -- md5sum > md5sum.txt ) if [ -n "$PARAMS" ]; then - # FIXME: make the script fail -- or maybe try to - # find a usable sed delimiter if $PARAMS contains a slash + case $PARAMS in + *@*) warn "PARAMS contains the @ sed delimiter, be sure it's escaped";; + esac echo ">>> Appending boot parameters" - sed -i 's/^ append .*$/& '"$PARAMS/" "$WDIR/cd-w/isolinux/isolinux.cfg" + sed -i 's@^ append .*$@& '"$PARAMS@" "$WDIR/cd-w/isolinux/isolinux.cfg" fi echo ">>> Creating ISO image $ISO" @@ -176,7 +209,7 @@ mkisofs \ -b isolinux/isolinux.bin \ -c isolinux/boot.cat \ -no-emul-boot -boot-load-size 4 -boot-info-table \ - -o "$ISO" \ + -o "$OUTPUT_FILE" \ $WDIR/cd-w # The trap ... callbacks will unmount everything. -- 1.6.1.rc1.279.g45d11
Perry Myers
2008-Dec-03 14:02 UTC
[Ovirt-devel] [PATCH node-image] edit-livecd: add -o OUTPUT.iso option, required
Jim Meyering wrote:> The new -o OUTPUT.iso option is now required. > > Allow the '-p CODE' option to specify bourne shell code, not just > a file/script. This permits a common idiom: to avoid writing a > separate shell script for a simple task, and instead include a > quoted multi-line snippet in the invoking script, e.g.,Question... Can you still pass a non bash script to -p like a python or ruby script? The reason why I left the script option as an 'arbitrary executable' instead of specifying bash was that I thought it would be useful for more complex operations for someone to use python instead of bash. Perry
Jim Meyering
2008-Dec-05 18:50 UTC
[Ovirt-devel] [PATCH node-image] edit-livecd: add -o OUTPUT.iso option, required
I've just pushed this, too: -------------- edit-livecd: add -o OUTPUT.iso option, required The new -o OUTPUT.iso option is now required. Allow the '-p CODE' option to specify bourne shell code, not just a file/script. This permits a common idiom: to avoid writing a separate shell script for a simple task, and instead include a quoted multi-line snippet in the invoking script, e.g., edit-livecd ...other-options... -p ' d=etc/foo mkdir -p $d && touch $d/file ' Also, in the boot params substitution, - use @ rather than / in the sed - warn if the selected string contains @ Update -h (help) output, and add examples. Evaluate $CODE in a subshell (safer and doesn't rely on pushd,popd). Guard against uninitialized ID_FS_LABEL. Don't use "function" keyword. Fail if there are any extra command-line arguments. Ensure that $PWD and $CD are double-eval safe (else fail), so that we don't have to quote them everywhere. Ensure that a $CODE (renamed from $PROG) definition doesn't leak in from the environment. --- edit-livecd | 117 ++++++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 75 insertions(+), 42 deletions(-) diff --git a/edit-livecd b/edit-livecd index ce9e65b..6d6498a 100755 --- a/edit-livecd +++ b/edit-livecd @@ -34,38 +34,49 @@ usage() { Usage: $ME -i LiveCD.iso [-b bootparams] [-p program] -b BOOTPARAMS optional parameters appended to the kernel command line -i LIVECD.iso LiveCD ISO to edit (default: $NODEIMG_DEFAULT) - -p PROGRAM Arbitrary program/script that is run inside of the livecd root - filesystem. This script is not run in a chroot environment so - it can access the host filesystem. The program should be executable - If program is omitted the script will pause and allow the user in - another terminal to edit the filesystem manually. After enter is - pushed the script will re-package up the ISO. + -o OUTPUT.iso specify the output file (required) + -p CODE Arbitrary CODE that is eval'd while 'cd'd into the root of + the livecd root filesystem. Note; the code is not run in + a chroot environment, so it can access the host filesystem. + If this option is omitted, this program pauses and allows + the user (in another terminal) to modify the filesystem + manually. Type <enter> when done, and the script + re-packages the ISO. -h display this help and exit +EXAMPLES + Example Script: #!/bin/sh touch etc/sysconfig/foo + Save as foo and make executable: + chmod a+x foo + Run this to create a file /etc/sysconfig/foo in the livecd filesystem + (note the use of "\$PWD/foo", not "./foo", since it will be run from a + different directory): + + $ME -i input.iso -o /tmp/result.iso -p "\$PWD/foo" + + or, equivalently, but without a separate script: + + $ME -i input.iso -o /tmp/result.iso -p 'touch etc/sysconfig/foo' - This will create a file /etc/sysconfig/foo in the livecd filesystem. EOF } -# FIXME: instead of requiring a PROGRAM, allow arbitrary shell code, -# so we can invoke $0 -p 'touch etc/sysconfig/foo' ... -# This means that if you *do* have a program in the current directory -# running "$0 -p ./my-script" won't work, since it'll be run with -# a different working dir. However, *this* will: -# $0 -p "$PWD/my-script" - # exit after any error: set -e +CODE+OUTPUT_FILE+ err=0 help=0 -while getopts :b:i:p:h c; do +while getopts :b:hi:o:p: c; do case $c in i) CD=$OPTARG;; b) PARAMS=$OPTARG;; - p) PROG=$OPTARG;; + o) OUTPUT_FILE=$OPTARG;; + p) CODE=$OPTARG;; h) help=1;; '?') err=1; warn "invalid option: \`-$OPTARG'";; :) err=1; warn "missing argument to \`-$OPTARG' option";; @@ -75,39 +86,59 @@ done test $err = 1 && { try_h; exit 1; } test $help = 1 && { usage; exit 0; } +# Require "-o OUTPUT_FILE" +test -z "$OUTPUT_FILE" \ + && { warn "no output file specified; use -o FILE.iso"; try_h; exit 1; } + +# Fail if there are any extra command-line arguments. +if test $OPTIND -le $#; then + bad_arg=$(eval "echo \$$OPTIND") + warn "extra argument '$bad_arg'"; try_h; exit 1 +fi + # first, check to see we are root if [ $( id -u ) -ne 0 ]; then die "Must run as root" fi -# FIXME: don't use which: with bash, it's a separate program -# FIXME: When failing due to a missing required program, tell the user why. -which mkisofs mksquashfs sed > /dev/null 2>&1 +# Check for some prerequisites. +# "type" prints "PROG not found" if it's not in $PATH. +type mkisofs +type mksquashfs +type sed + +sane_name() +{ + case $1 in + *[^a-zA-Z0-9._,+:/@%=-]*) false;; + *) true;; + esac +} + +# Fail if names we'll use contain white space or shell meta-characters +sane_name "$PWD" || die "invalid working directory name: $PWD" +sane_name "$CD" || die "invalid ISO name: $CD" WDIR=`mktemp -d $PWD/livecd.XXXXXXXXXX` -# FIXME: fail if $WDIR contains white space or shell meta-characters -# FIXME: do the same for $CD. - -ISO="${CD##*/}" -ISO="${ISO%.iso}-custom.iso" -function addExit() { +addExit() { EXIT="$@ ; $EXIT" trap "$EXIT" EXIT HUP TERM INT QUIT } -function mnt() { +mnt() { local margs="$1" ; shift local mp="$WDIR/$1" for D in "$@" ; do mkdir -v -p "$WDIR/$D" done - mount -v $margs "$mp" + eval mount -v $margs "$mp" addExit "df | grep $mp > /dev/null 2>&1 && umount -v $mp" } addExit "rm -rf $WDIR" +ID_FS_LABEL= # initialize, in case vol_id fails eval "$(/lib/udev/vol_id $CD)" LABEL=$ID_FS_LABEL @@ -120,21 +151,21 @@ mnt "-t squashfs $WDIR/cd/LiveOS/squashfs.img -o ro,loop" sq # create writable copy of the new filesystem for the CD cp -a $WDIR/cd $WDIR/cd-w -# create writable copy of the filesystem for the new compressed squashfs filesystem +# create writable copy of the filesystem for the new compressed +# squashfs filesystem cp -a $WDIR/sq $WDIR/sq-w # mount ext3 filesystem mnt "-t auto $WDIR/sq-w/LiveOS/ext3fs.img -o rw,loop" ex echo ">>> Updating CD content" -if [ -n "$PROG" ]; then - cp -av $PROG $WDIR/ex - pushd $WDIR/ex - set +e - ./$(basename $PROG) - set -e - popd - rm -f $WDIR/ex/$PROG +if [ -n "$CODE" ]; then + ( + cd $WDIR/ex + set +e + eval "$CODE" + set -e + ) else echo "***" echo "*** Pausing to allow manual changes. Press any key to continue." @@ -147,7 +178,7 @@ fi while :; do echo ">>> Unmounting ext3fs" umount $WDIR/ex && break - echo ">>> Unmounting ext3fs failed" + echo ">>> Unmounting the working file system copy failed" echo "***" echo "*** Did you forget to 'cd' out of $WDIR/ex?" echo "***" @@ -160,13 +191,15 @@ echo ">>> Compressing filesystem" mksquashfs $WDIR/sq-w/ $WDIR/cd-w/LiveOS/squashfs.img -noappend echo ">>> Recomputing MD5 sums" -( cd $WDIR/cd-w && find . -type f -not -name md5sum.txt -not -path '*/isolinux/*' -print0 | xargs -0 -- md5sum > md5sum.txt ) +( cd $WDIR/cd-w && find . -type f -not -name md5sum.txt \ + -not -path '*/isolinux/*' -print0 | xargs -0 -- md5sum > md5sum.txt ) if [ -n "$PARAMS" ]; then - # FIXME: make the script fail -- or maybe try to - # find a usable sed delimiter if $PARAMS contains a slash + case $PARAMS in + *@*) warn "PARAMS contains the @ sed delimiter, be sure it's escaped";; + esac echo ">>> Appending boot parameters" - sed -i 's/^ append .*$/& '"$PARAMS/" "$WDIR/cd-w/isolinux/isolinux.cfg" + sed -i 's@^ append .*$@& '"$PARAMS@" "$WDIR/cd-w/isolinux/isolinux.cfg" fi echo ">>> Creating ISO image $ISO" @@ -176,7 +209,7 @@ mkisofs \ -b isolinux/isolinux.bin \ -c isolinux/boot.cat \ -no-emul-boot -boot-load-size 4 -boot-info-table \ - -o "$ISO" \ + -o "$OUTPUT_FILE" \ $WDIR/cd-w # The trap ... callbacks will unmount everything. -- 1.6.1.rc1.333.gba3cb