drakkar-lig / debootstick

Generate a bootable live image from any Debian/Ubuntu filesystem tree.
62 stars 18 forks source link

errors in quiet_grub_install() are getting ignored during image creation #10

Closed unki closed 7 years ago

unki commented 7 years ago

In scripts/create-image/functions function quiet_grub_install() is used to suppress the normal output of grub-install, update-grub, etc.

I've noted, that if an error occurs eg. during grub-install, debootstick will continue to work and does not recognize occurring problems.

I: final image - copying content from draft image... done
I: final image - setting up the bootloader... grub-install: error: cannot write to `/dev/loop1': Input/output error.
I: final image - setting up the EFI boot partition... done
I: cleaning up... done
I: usbstick.img ready (size: 1.1G). 

Actually the image is broken and can not be used to boot of. Basically this block in quiet_grub_install() should have taken care about it:

 {
        grub-install $device    && \
        update-initramfs -u
        update-grub
        return_code=$?
    } 2>&1 |    happy_grep -v "No error"          | \
                happy_grep -v "Installing"        | \
                happy_grep -v "Generating"        | \
                happy_grep -v "Found .* image:"   | \
                happy_grep -v "lvmetad"           | \
                happy_grep -v "etc.modprobe.d"    | \
                happy_grep -v "^done$" 1>&2

But as far as I found out, pipelining causes subshells to be used (eg. man 1 bash)...

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

... the exit-code that have been stored at return code=$? within the command group {...} seems to be lost after pipelining and at return $return_code it is simply empty - it does not contain an exit-code at all. By this no hint about the error is returned to the caller of quiet_grub_install().

On removing pipelining to happy_grep calls, quiet_grub_install() would indeed correctly stop the image generation on receiving eg. a grub-install error:

I: draft image - installing packages: linux-image-amd64 lvm2 gdisk grub-pc... done
I: draft image - setting up bootloader... Installing for i386-pc platform.
grub-install: error: cannot write to `/dev/loop0': Input/output error.

E: an error occured.
E: did you try 'debootstick --help-os-support'?
I: restoring a clean state... done
Makefile:50: recipe for target 'build' failed
make: *** [build] Error 1

A workaround would be to use a temp file to capture the output and letting the happy_grep's to match on that one instead.

    local TMPFILE="$(mktemp)"
    # grub-install & update-grub print messages to standard
    # error stream although most of these are just
    # informational (or minor bugs). Let's discard them.
    {   
        grub-install $device && \
        update-initramfs -u && \
        update-grub;
        return_code=$?;
    } >$TMPFILE 2>&1

    happy_grep -v "No error" $TMPFILE | \
    happy_grep -v "Installing"        | \
    happy_grep -v "Generating"        | \
    happy_grep -v "Found .* image:"   | \
    happy_grep -v "lvmetad"           | \
    happy_grep -v "etc.modprobe.d"    | \
    happy_grep -v "^done$" 1>&2
    rm $TMPFILE

It's not that beautiful than pipelining, otherwise it works and does not effect $return_code somehow :-)

eduble commented 7 years ago

Thanks for reporting. I will look at it in more details. BTW, do you know what is the reason for the Input/output error?

eduble commented 7 years ago

I pushed the following fix:

    # grub-install & update-grub print messages to standard
    # error stream although most of these are just
    # informational (or minor bugs). Let's discard them.
    output="$(
        grub-install $device 2>&1   && \
        update-initramfs -u 2>&1    && \
        update-grub 2>&1
    )" || return_code=$?

    echo "$output" |    happy_grep -v "No error"          | \
                        happy_grep -v "Installing"        | \
                        happy_grep -v "Generating"        | \
                        happy_grep -v "Found .* image:"   | \
                        happy_grep -v "lvmetad"           | \
                        happy_grep -v "etc.modprobe.d"    | \
                        happy_grep -v "^done$" 1>&2

    # the return value we want is the one we caught
    # earlier (or none if all went well):
    return $return_code
unki commented 7 years ago

BTW, do you know what is the reason for the Input/output error?

It was just the host's filesystem that was running out of free space while debootstick was working on grub-install. That is probably a very particular case as all the previous tasks that really consumes a lot of disk space must have succeed and only this final grub-install then fails - maybe while copying its stuff into the /boot.

Anyway - your fix works like a charm! Just retriggered that error and it stops as it should on detecting an error:

I: final image - setting up the bootloader... grub-install: error: cannot write to `/dev/loop1': Input/output error.

E: an error occured.
E: did you try 'debootstick --help-os-support'?
I: restoring a clean state... done
Makefile:50: recipe for target 'build' failed
make: *** [build] Error 1

Thanks!