Subject: bin/35547: add timeout to cgdconfig
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: None <anne@encs.concordia.ca>
List: netbsd-bugs
Date: 02/04/2007 17:55:00
>Number:         35547
>Category:       bin
>Synopsis:       add timeout to cgdconfig
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Feb 04 17:55:00 +0000 2007
>Originator:     Anne Bennett
>Release:        3.1
>Organization:
>Environment:
NetBSD quill.porcupine.qc.ca 3.1 NetBSD 3.1 (QUILL_I386) #1: Thu Jan 18 23:29:30 EST 2007  anne@quill.porcupine.qc.ca:/usr/src/sys/arch/i386/compile/QUILL_I386 i386

>Description:
Modify cgdconfig to allow a timeout (e.g. option "-t seconds"),
or modify /etc/rc.d/cgd to check for user presence before trying
cgdconfig.  In other words, if there's no user input in some
number of seconds, give up and exit.

Reason: if a system has to boot without the sysadmin present, it
should be possible for it to come up without the CGDs (assuming
of course that the sysadmin didn't put any boot-critical data
on a CGD!), as opposed to stalling the boot indefinitely while
it waits for a human to show up and type in the password.

For example, there is only a small amount of sensitive data on
my workstation, and if there's a crash or a power failure while
I'm away, I'd like the system to come up; I can then log in 
remotely and bring up the CGD (from a trusted system, of course!).
It would be very inconvenient to have to do without the system
entirely until I can be physically present to help it boot, just
because I have a little bit of encrypted data.

I've managed to hack this in a rather ugly way for now (using
/etc/rc.d/cgd), but it would be ever so much better to just be
able to add 'cgd_flags="-t 15"' to rc.local, for example.  (Not
being a frequent C programmer, I didn't try such a patch myself.)

Note that I've had to make some additional changes to make this 
work, and something like them would also be required for a cleaner
implementation: (a) make cgd filesystems "noauto" in fstab to avoid
unintended mounts [or you could just let them fail], (b) put "0"
in the "fsck pass" field in fstab, otherwise an attempt will be made
to fsck the filesystem on the unconfigured device, and the boot will 
fail, (c) added a setting cgd_bypass_noauto="list of mountpoints"
to rc.local, to permit cgd filesystems to be found, fsck'd, and 
mounted but only if the underlying device is configured, (d) added
a script /etc/rc.d/mountcgdbypass to do this.

This seems Rube-Goldbergesque, and I can't help but think there
must be a better way.  I could probably simplify it considerably
if I never fsck'd the CGD filesystems, for example: then it would
be sufficient to let the mount attempts (of the unconfigured device)
fail and the boot continue.  It would be neat of there were an fstab
option that made it possible to fsck a filesystem only if its
underlying device was configured, but I have no idea how difficult
that would be.

So for now, my request is basically to add a timeout option to
cdgconfig.  If anyone else finds this idea useful and would like
to "fix it", that would be amazing.

Anne.

>How-To-Repeat:
Try to boot a system which wants to mount cgd filesystems
during the boot sequence, but don't enter the cgd password.
The boot will stall.
>Fix:
Here are some horrible hacks.  There has to be a better way.

-------------------------------------------------------
#!/bin/sh
#
# $NetBSD: cgd,v 1.5 2005/03/02 19:09:22 tv Exp $
#

# PROVIDE: disks

# 2007/01/21 Anne Bennett: proceed only if user standing by with password.
#  This avoids indefinite boot hang if no on is there.
#  WARNING: make sure no attempt is made to "fsck" filesystems on
#  potentially unconfigured devices.
timeout=15

$_rc_subr_loaded . /etc/rc.subr

name="cgd"
rcvar=$name
start_cmd="cgd_start"
stop_cmd=":"

cgd_start()
{
        if [ -f /etc/cgd/cgd.conf ]; then
                answer=""
                echo    "^GAre you ready to configure the CGD?"
                echo -n "^GPlease respond within $timeout seconds (y or n): "

                # Timeout for stty is in tenths of a second:
                stty_to=`expr 10 \* $timeout`
                # Keep old tty settings:
                oldtty="`stty -g`"
                # Turn off canonical mode, turn off signals, enforce timeout:
                stty -icanon min 0 time $stty_to -isig
                # Try to get user input:
                read answer
                # Put terminal back to normal:
                stty "$oldtty"

                if [ -z $answer ] ; then 
                        echo "No response within $timeout seconds; skipping CGD 
configuration."
                else
                        if expr "$answer" : "[Yy]" >/dev/null ; then
                                echo "Configuring CGD devices."
                                cgdconfig -C
                        else
                                echo "Okay, skipping CGD configuration."
                        fi
                fi 
        
        fi
}

load_rc_config $name
run_rc_command "$1"

-------------------------------------------------------
#!/bin/sh
#
# $NetBSD: mountcgdbypass,v 1.0 2007/01/21 15:00:00 anne Exp $
#

# REQUIRE: disks
# BEFORE:  DAEMON

$_rc_subr_loaded . /etc/rc.subr

name="mountcgdbypass"
start_cmd="mountcgdbypass_start"
stop_cmd=":"

mountcgdbypass_start()
{
        #       Mount `cgd' filesystems specified in $cgd_bypass_noauto,
        #       but only if their cgd is configured.
        #

        if [ -z "$cgd_bypass_noauto" ]; then
                exit
        fi

        # Of the specified list, keep only those not already mounted.
        mount_candidates="`
        for _fs in $cgd_bypass_noauto; do
                mount | (
                        _ismounted=false
                        while read what _on on _type type; do
                                if [ $on = $_fs ]; then
                                        _ismounted=true
                                fi
                        done
                        if $_ismounted; then
                                :
                        else
                                echo $_fs
                        fi
                )
        done`"
        mount_candidates="`echo $mount_candidates`"

        if [ -z "$mount_candidates" ]; then
                exit
        fi

        # Of the remaining candidates, keep only those whose dev is configured.
        configured_disks="`sysctl -n hw.disknames`"
        ready_candidates="`
        for disk in $configured_disks ; do
                ( while read fs mtpt type opt fsck1 fsck2 ; do
                        case $fs in
                                /dev/$disk[a-p])
                                        if expr \" $mount_candidates \" : \".* $
mtpt \" > /dev/null ; then
                                                echo $mtpt
                                        fi
                                        ;;
                                *)
                                        ;;
                        esac
                done ) < /etc/fstab
        done`"
        ready_candidates="`echo $ready_candidates`"


        # For the remaining candidates, fsck and mount if okay
        for fs in $ready_candidates ; do
                echo Checking and mounting bypassed CGD filesystem $fs
                fsck -p $fs && mount $fs
        done

}

load_rc_config $name
run_rc_command "$1"

-------------------------------------------------------