From: Philippe Seewer Date: Tue, 16 Jun 2009 16:25:16 +0000 (+0200) Subject: netroot: Introduce detailed cmdline parsers X-Git-Tag: 0.1~41 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=50e7ff76de1b9179565f011cd44ea669311e132d;p=platform%2Fupstream%2Fdracut.git netroot: Introduce detailed cmdline parsers This introduces detailed cmdline parsing, warning or aborting if the cmdline does not contain arguments according to the spec. Makeing sure the parsers don't just call getarg for netroot et al, allows their reuse inside netroot to analyse dhcp root-path as well. Hence we can get rid of the current netroot hooks. The hook itself stays in order to add further modules which should run before netroot handlers are called. This has one drawback: nfsroot needs some more logic to handle nfs specific data inside dhcp root-path. The parsers have been writting according to current discussions about cmdline arguments. This lead to the "discovery" that some test-cases violate the spec. These tests have not been removed, but change to "must fail". --- diff --git a/modules.d/40network/dhcp-fallback.sh b/modules.d/40network/dhcp-fallback.sh index 6bc823f..7843640 100755 --- a/modules.d/40network/dhcp-fallback.sh +++ b/modules.d/40network/dhcp-fallback.sh @@ -1,16 +1,23 @@ # We should go last, and default the root if needed if [ -z "$root" -a -z "$netroot" ]; then + rootok=1 + root=dhcp netroot=dhcp fi if [ "$root" = "dhcp" -a -z "$netroot" ]; then rootok=1 netroot=dhcp - unset root fi -# Cleanup any coversions from root->netroot if they are the same -if [ "$netroot" = "$root" ]; then - unset root +if [ "$netroot" = "dhcp" -a -z "$root" ]; then + rootok=1 + root=dhcp +fi + +if [ -n "$NEEDDHCP" ] ; then + rootok=1 + root=dhcp + #Don't overwrite netroot here, as it might contain something useful fi diff --git a/modules.d/40network/netroot b/modules.d/40network/netroot index 10a50c7..b35c6c4 100755 --- a/modules.d/40network/netroot +++ b/modules.d/40network/netroot @@ -32,19 +32,39 @@ fi netif=$1 -[ -e /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts +# Figure out the handler for root=dhcp by recalling all netroot cmdline +# handlers +if [ "$netroot" = "dhcp" ] ; then + # Unset root so we can check later + unset root -# Now, let the installed network root handlers figure this out -# -source_all netroot + # Load dhcp options + [ -e /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts -# If we didn't get a handler set, then we're done -# -if [ -z "$handler" ]; then - # XXX informative error message? - exit 0 + # Set netroot to new_root_path, so cmdline parsers don't call + netroot=$new_root_path + + for f in ./cmdline/90*.sh; do + [ -f "$f" ] && . "$f"; + done +else + rootok="1" fi +# Check: do we really know how to handle (net)root? +[ -z "$root" ] && die "No or empty root= argument" +[ -z "$rootok" ] && die "Don't know how to handle 'root=$root'" + +handler=${netroot%%:*} +handler=${handler%%4} +handler="/sbin/${handler}root" +if [ -z "$netroot" ] || [ ! -e "$handler" ] ; then + die "No handler for netroot type '$netroot'" +fi + +# Source netroot hooks before we start the handler +source_all netroot + # Run the handler; don't store the root, it may change from device to device # XXX other variables to export? if $handler $netif $netroot $NEWROOT; then diff --git a/modules.d/95iscsi/install b/modules.d/95iscsi/install index cd4a3ca..613c752 100755 --- a/modules.d/95iscsi/install +++ b/modules.d/95iscsi/install @@ -4,6 +4,5 @@ inst iscsistart inst hostname inst iscsi-iname inst_hook cmdline 90 "$moddir/parse-iscsiroot.sh" -inst_hook netroot 90 "$moddir/iscsi-netroot.sh" inst "$moddir/iscsiroot" "/sbin/iscsiroot" instmods iscsi_tcp crc32c diff --git a/modules.d/95iscsi/iscsi-netroot.sh b/modules.d/95iscsi/iscsi-netroot.sh deleted file mode 100755 index 4f02239..0000000 --- a/modules.d/95iscsi/iscsi-netroot.sh +++ /dev/null @@ -1,13 +0,0 @@ -if [ "$root" = "dhcp" ]; then - if [ -n "$new_root_path" -a -z "${new_root_path%%iscsi:*}" ]; then - root="$new_root_path" - fi -fi - -if [ -z "${root%iscsi:*}" ]; then - handler=/sbin/iscsiroot -fi - -if getarg iscsiroot >/dev/null; then - handler=/sbin/iscsiroot -fi diff --git a/modules.d/95iscsi/iscsiroot b/modules.d/95iscsi/iscsiroot index f848aba..e273fc5 100755 --- a/modules.d/95iscsi/iscsiroot +++ b/modules.d/95iscsi/iscsiroot @@ -10,8 +10,6 @@ PATH=$PATH:/sbin:/usr/sbin -# XXX needs error handling like ifup/dhclient-script - if getarg rdnetdebug; then exec > /tmp/iscsiroot.$1.$$.out exec 2>> /tmp/iscsiroot.$1.$$.out @@ -37,7 +35,11 @@ for conf in conf/conf.d/*; do [ -f ${conf} ] && . ${conf} done -modprobe iscsi_tcp +# If it's not iscsi we don't continue +[ "${root%%:*}" = "iscsi" ] || exit 1 + +# XXX modprobe crc32c should go in the cmdline parser, but I haven't yet +# figured out a way how to check whether this is built-in or not modprobe crc32c if getarg iscsi_firmware ; then diff --git a/modules.d/95iscsi/parse-iscsiroot.sh b/modules.d/95iscsi/parse-iscsiroot.sh index 4115248..1ba4277 100755 --- a/modules.d/95iscsi/parse-iscsiroot.sh +++ b/modules.d/95iscsi/parse-iscsiroot.sh @@ -1,32 +1,75 @@ -# XXX actually we could, if we move to root=XXX and netroot=XXX, then -# you could do root=LABEL=/ iscsiroot=XXX, or netroot=iscsi:XXX -# +#!/bin/sh # # Preferred format: -# root=iscsi:[]:[]:[]:[]: +# root=iscsi:[]:[]:[]:[]: +# [root=*] netroot=iscsi:[]:[]:[]:[]: # # Legacy formats: -# iscsiroot=[]:[]:[]:[]: -# root=dhcp iscsiroot=[]:[]:[]:[]: -# root=iscsi iscsiroot=[]:[]:[]:[]: -# root=??? iscsi_initiator= iscsi_target_name= iscsi_target_ip= iscsi_target_port= iscsi_target_group= iscsi_username= iscsi_password= iscsi_in_username= iscsi_in_password= -# root=??? iscsi_firmware - -case "$root" in - iscsi|dhcp|'') - if getarg iscsiroot= > /dev/null; then - root=iscsi:$(getarg iscsiroot=) - fi - ;; -esac - -if [ "${root%%:*}" = "iscsi" ]; then - # XXX validate options here? - # XXX generate udev rules? - rootok=1 - netroot=iscsi +# [net]root=[iscsi] iscsiroot=[]:[]:[]:[]: +# [net]root=[iscsi] iscsi_firmware +# +# root= takes precedence over netroot= if root=iscsi[...] +# + +# Don't continue if root is ok +[ -n "$rootok" ] && return + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) +[ -z "$netroot" ] && netroot=$(getarg netroot=) +[ -z "$iscsiroot" ] && iscsiroot=$(getarg iscsiroot=) +[ -z "$iscsi_firmware" ] && getarg iscsi_firmware && iscsi_firmware="1" + +[ -n "$iscsiroot" ] && [ -n "$iscsi_firmware" ] && die "Mixing iscsiroot and iscsi_firmware is dangerous" + +# Root takes precedence over netroot +if [ "${root%%:*}" = "iscsi" ] ; then + if [ -n "$netroot" ] ; then + echo "Warning: root takes precedence over netroot. Ignoring netroot" + + fi + netroot=$root +fi + +# If it's not empty or iscsi we don't continue +[ -z "$netroot" ] || [ "${netroot%%:*}" = "iscsi" ] || return + +if [ -n "$iscsiroot" ] ; then + [ -z "$netroot" ] && netroot=$root + + # @deprecated + echo "Warning: Argument isciroot is deprecated and might be removed in a future" + echo "release. See http://apps.sourceforge.net/trac/dracut/wiki/commandline for" + echo "more information." + + # Accept iscsiroot argument? + [ -z "$netroot" ] || [ "$netroot" = "iscsi" ] || \ + die "Argument iscsiroot only accepted for empty root= or [net]root=iscsi" + + # Override root with iscsiroot content? + [ -z "$netroot" ] || [ "$netroot" = "iscsi" ] && netroot=iscsi:$iscsiroot +fi + +# iscsi_firmware does not need argument checking +if [ -n "$iscsi_firmware" ] ; then + netroot=${netroot:-iscsi} fi -if getarg iscsiroot; then - netroot=iscsi +# If it's not iscsi we don't continue +[ "${netroot%%:*}" = "iscsi" ] || return + +# Check required arguments. there's only one, but it's at the end +if [ -z "$iscsi_firmware" ] ; then + case "${netroot##iscsi:*:*:*:*:}" in + $netroot|'') die "Argument targetname for iscsiroot is missing";; + esac fi + +# ISCSI actually supported? +[ -e /sys/devices/virtual/iscsi_transport ] || modprobe iscsi_tcp || die "iscsiroot requested but kernel/initrd does not support iscsi" + +# Done, all good! +rootok=1 + +# Shut up init error check +[ -z "$root" ] && root="iscsi" diff --git a/modules.d/95nbd/install b/modules.d/95nbd/install index 3a80da1..eca74e4 100755 --- a/modules.d/95nbd/install +++ b/modules.d/95nbd/install @@ -2,6 +2,5 @@ inst nbd-client inst_hook cmdline 90 "$moddir/parse-nbdroot.sh" -inst_hook netroot 90 "$moddir/nbd-netroot.sh" inst "$moddir/nbdroot" "/sbin/nbdroot" instmods nbd diff --git a/modules.d/95nbd/nbd-netroot.sh b/modules.d/95nbd/nbd-netroot.sh deleted file mode 100755 index 9513982..0000000 --- a/modules.d/95nbd/nbd-netroot.sh +++ /dev/null @@ -1,9 +0,0 @@ -if [ "$netroot" = "dhcp" ]; then - if [ -n "$new_root_path" -a -z "${new_root_path%%nbd:*}" ]; then - netroot="$new_root_path" - fi -fi - -if [ -z "${netroot%nbd:*}" ]; then - handler=/sbin/nbdroot -fi diff --git a/modules.d/95nbd/nbdroot b/modules.d/95nbd/nbdroot index 91f6192..71f6af9 100755 --- a/modules.d/95nbd/nbdroot +++ b/modules.d/95nbd/nbdroot @@ -4,8 +4,6 @@ PATH=$PATH:/sbin:/usr/sbin -# XXX needs error handling like ifup/dhclient-script - if getarg rdnetdebug; then exec > /tmp/nbdroot.$1.$$.out exec 2>> /tmp/nbdroot.$1.$$.out @@ -21,11 +19,14 @@ fi # Huh? Empty $3? [ -z "$3" ] && exit 1 -# root is in the form root=nbd:server:port:fstype:fsopts:nbdopts +# root is in the form root=nbd:srv:port[:fstype[:rootflags[:nbdopts]]] netif="$1" root="$2" NEWROOT="$3" +# If it's not nbd we don't continue +[ "${root%%:*}" = "nbd" ] || return + root=${root#nbd:} nbdserver=${root%%:*}; root=${root#*:} nbdport=${root%%:*}; root=${root#*:} @@ -81,8 +82,6 @@ getarg ro && nbdrw=ro getarg rw && nbdrw=rw fsopts=${fsopts+$fsopts,}${nbdrw} -incol2 /proc/devices nbd || modprobe nbd || exit 1 - # XXX better way to wait for the device to be made? i=0 while [ ! -b /dev/nbd0 ]; do diff --git a/modules.d/95nbd/parse-nbdroot.sh b/modules.d/95nbd/parse-nbdroot.sh index 426d281..6549b67 100755 --- a/modules.d/95nbd/parse-nbdroot.sh +++ b/modules.d/95nbd/parse-nbdroot.sh @@ -1,51 +1,81 @@ -# It'd be nice if this could share rules with 99-block.sh, but since -# the kernel side adds nbd{1..16} when the module is loaded -- before -# they are associated with a server -- we cannot use the udev add rule -# to find it -# -# XXX actually we could, if we move to root=XXX and netroot=XXX, then -# you could do root=LABEL=/ nbdroot=XXX, or netroot=nbd:XXX -# -# However, we need to be 90-nbd.sh to catch root=/dev/nbd* +#!/bin/sh # # Preferred format: # root=nbd:srv:port[:fstype[:rootflags[:nbdopts]]] +# [root=*] netroot=nbd:srv:port[:fstype[:rootflags[:nbdopts]]] # -# nbdopts is a comma seperated list of options to give to nbd-client +# Legacy formats: +# [net]root=[nbd] nbdroot=srv,port +# [net]root=[nbd] nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]] # +# nbdopts is a comma seperated list of options to give to nbd-client # -# Legacy formats: -# nbdroot=srv,port -# nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]] -# root=dhcp nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]] -# root=nbd nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]] +# root= takes precedence over netroot= if root=nbd[...] # -case "$root" in - nbd|dhcp|'') - if getarg nbdroot= > /dev/null; then - root=nbd:$(getarg nbdroot=) - fi - ;; -esac - -# Convert the Debian style to our syntax, but avoid matches on fs arguments -case "$root" in - nbd:*,*) - if check_occurances "$root" ',' 1 && check_occurances "$root" ':' 1; - then - root=${root%,*}:${root#*,} - fi - ;; -esac - -if [ -z "$netroot" -a -n "$root" -a -z "${root%%nbd:*}" ]; then - netroot="$root" - unset root +# Sadly there's no easy way to split ':' separated lines into variables +netroot_to_var() { + local v=${1}: + set -- + while [ -n "$v" ]; do + set -- "$@" "${v%%:*}" + v=${v#*:} + done + + unset server port + server=$2; port=$3; +} + +# Don't continue if root is ok +[ -n "$rootok" ] && return + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) +[ -z "$netroot" ] && netroot=$(getarg netroot=) +[ -z "$nbdroot" ] && nbdroot=$(getarg nbdroot=) + +# Root takes precedence over netroot +if [ "${root%%:*}" = "nbd" ] ; then + if [ -n "$netroot" ] ; then + warn "root takes precedence over netroot. Ignoring netroot" + + fi + netroot=$root fi -if [ "${netroot%%:*}" = "nbd" ]; then - # XXX validate options here? - # XXX generate udev rules? - rootok=1 +# If it's not empty or nbd we don't continue +[ -z "$netroot" ] || [ "${netroot%%:*}" = "nbd" ] || return + +if [ -n "$nbdroot" ] ; then + [ -z "$netroot" ] && netroot=$root + + # Debian legacy style contains no ':' Converting is easy + [ "$nbdroot" = "${nbdroot##*:}" ] && nbdroot=${nbdroot%,*}:${nbdroot#*,} + + # @deprecated + warn "Argument nbdroot is deprecated and might be removed in a future release. See http://apps.sourceforge.net/trac/dracut/wiki/commandline for more information." + + # Accept nbdroot argument? + [ -z "$netroot" ] || [ "$netroot" = "nbd" ] || \ + die "Argument nbdroot only accepted for empty root= or [net]root=nbd" + + # Override netroot with nbdroot content? + [ -z "$netroot" ] || [ "$netroot" = "nbd" ] && netroot=nbd:$nbdroot fi + +# If it's not nbd we don't continue +[ "${netroot%%:*}" = "nbd" ] || return + +# Check required arguments +netroot_to_var $netroot +[ -z "$server" ] && die "Argument server for nbdroot is missing" +[ -z "$port" ] && die "Argument port for nbdroot is missing" + +# NBD actually supported? +incol2 /proc/devices nbd || modprobe nbd || die "nbdroot requested but kernel/initrd does not support nbd" + +# Done, all good! +rootok=1 + +# Shut up init error check +[ -z "$root" ] && root="nbd" diff --git a/modules.d/95nfs/install b/modules.d/95nfs/install index a8e3ef1..5cf0ff6 100755 --- a/modules.d/95nfs/install +++ b/modules.d/95nfs/install @@ -18,7 +18,6 @@ dracut_install $(ls {/usr,}$LIBDIR/libnss*.so 2>/dev/null) instmods nfs sunrpc ipv6 inst_hook cmdline 90 "$moddir/parse-nfsroot.sh" -inst_hook netroot 90 "$moddir/nfs-netroot.sh" inst_hook pre-pivot 70 "$moddir/nfsroot-cleanup.sh" inst "$moddir/nfsroot" "/sbin/nfsroot" mkdir -p "$initdir/var/lib/nfs/rpc_pipefs" diff --git a/modules.d/95nfs/nfs-netroot.sh b/modules.d/95nfs/nfs-netroot.sh deleted file mode 100755 index 71ab00d..0000000 --- a/modules.d/95nfs/nfs-netroot.sh +++ /dev/null @@ -1,67 +0,0 @@ -# If we're auto-detecting our root type from DHCP, see if this looks like -# an NFS root option. As the variety of root-path formats is large, validate -# that the number of colons match what we expect, and our glob didn't -# inadvertently match a different handler's. -# -if [ "$netroot" = "dhcp" -o "$netroot" = "nfs" -o "$netroot" = "nfs4" ]; then - nfsver=nfs - if [ "$netroot" = "nfs4" ]; then - nfsver=nfs4 - fi - case "$new_root_path" in - nfs:*|nfs4:*) netroot="$new_root_path" ;; - *:/*:*) - if check_occurances "$new_root_path" ':' 2; then - netroot="$nfsver:$new_root_path" - fi ;; - *:/*,*) - if check_occurances "$new_root_path" ':' 1; then - netroot="$nfsver:$new_root_path" - fi ;; - *:/*) - if check_occurances "$new_root_path" ':' 1; then - netroot="$nfsver:$new_root_path:" - fi ;; - /*:*) - if check_occurances "$new_root_path" ':' 1; then - netroot="$nfsver::$new_root_path" - fi ;; - /*,*) - if check_occurances "$new_root_path" ':' 0; then - netroot="$nfsver::$new_root_path" - fi ;; - /*) - if check_occurances "$new_root_path" ':' 0; then - netroot="$nfsver::$new_root_path:" - fi ;; - '') netroot="$nfsver:::" ;; - esac -fi - -if [ -z "${netroot%%nfs:*}" -o -z "${netroot%%nfs4:*}" ]; then - # Fill in missing information from DHCP - nfsver=${netroot%%:*}; netroot=${netroot#*:} - nfsserver=${netroot%%:*}; netroot=${netroot#*:} - nfspath=${netroot%%:*} - nfsflags=${netroot#*:} - - # XXX where does dhclient stash the next-server option? Do we care? - if [ -z "$nfsserver" -o "$nfsserver" = "$nfspath" ]; then - nfsserver=$new_dhcp_server_identifier - fi - - # Handle alternate syntax of path,options - if [ "$nfsflags" = "$nfspath" -a "${netroot#*,}" != "$netroot" ]; then - nfspath=${netroot%%,*} - nfsflags=${netroot#*,} - fi - - # Catch the case when no additional flags are set - if [ "$nfspath" = "$nfsflags" ]; then - unset nfsflags - fi - - # XXX validate we have all the required info? - netroot="$nfsver:$nfsserver:$nfspath:$nfsflags" - handler=/sbin/nfsroot -fi diff --git a/modules.d/95nfs/nfsroot b/modules.d/95nfs/nfsroot index feeea9e..46c2b42 100755 --- a/modules.d/95nfs/nfsroot +++ b/modules.d/95nfs/nfsroot @@ -1,11 +1,47 @@ #!/bin/sh +# Copy from parse-nfsroot.sh +root_to_var() { + local v=${1}: + set -- + while [ -n "$v" ]; do + set -- "$@" "${v%%:*}" + v=${v#*:} + done + + unset nfs server path options + + # Ugly: Can't -z test $path after the case, since it might be allowed + # to be empty for root=nfs + nfs=$1 + case $# in + 0|1);; + 2) path=$2;; + 3) + # This is ultra ugly. But we can't decide in which position path + # sits without checking if the string starts with '/' + case $2 in + /*) path=$2; options=$3;; + *) server=$2; path=$3;; + esac + ;; + *) server=$2; path=$3; options=$4; + esac + + # Does it really start with '/'? + [ -n "${path%%/*}" ] && path="error"; + + #Fix kernel legacy style separating path and options with ',' + if [ "$path" != "${path#*,}" ] ; then + options=${path#*,} + path=${path%%,*} + fi +} + . /lib/dracut-lib PATH=$PATH:/sbin:/usr/sbin -# XXX needs error handling like ifup/dhclient-script - if getarg rdnetdebug ; then exec > /tmp/nfsroot.$1.$$.out exec 2>> /tmp/nfsroot.$1.$$.out @@ -21,65 +57,89 @@ fi # Huh? Empty $3? [ -z "$3" ] && exit 1 -# root is in the form root=nfs[4]:server:path:[options] +# root is in the form root=nfs[4]:[server:]path[:options], either from +# cmdline or dhcp root-path netif="$1" root="$2" NEWROOT="$3" -nfsver=${root%%:*}; root=${root#*:} -nfsserver=${root%%:*}; root=${root#*:} -nfspath=${root%%:*} -flags=${root#*:} +# If it's not nfs we don't continue +case "${root%%:*}" in + nfs|nfs4);; + *) return;; +esac + +# Ugly: root=nfs[4] requires dhcp root-path +if [ "$root" = "nfs" ] || [ "$root" = "nfs4" ] ; then + # No need to check if the file exists. ip cmdline parser takes care of this + . /tmp/dhclient.$netif.dhcpopts + [ -z "$new_root_path" ] && die "Required dhcp option root-path not available" + root=$root:$new_root_path +fi + +root_to_var $root + +#Load other data that might provide info +[ -f /tmp/net.$netif.override ] && . /tmp/net.$netif.override +[ -f /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts + +#Empty path means try dhcp root-path, this is ok here since parse-nfsroot.sh +#already takes care of nfs:... formatted root-path +[ -z "$path" ] && root_to_var $nfs:$new_root_path + +#Empty path defaults to "/tftpboot/%s" +[ -z "$path" ] && path="/tftpboot/%s" + +if [ -z "$server" ] ; then + # XXX new_dhcp_next_server is unconfirmed this is an assumption + for var in $srv $new_dhcp_server_identifier $new_dhcp_next_server $new_root_path '' ; do + [ -n "$var" ] && server=$var && break; + done + + # XXX This blindly assumes that if new_root_path has to used that + # XXX it really can be used as server + server=${server%%:*} +fi -[ -z "$nfspath" ] && nfspath=/tftpboot/%s +[ -z "$server" ] && die "Required parameter 'server' is missing" # Kernel replaces first %s with host name, and falls back to the ip address # if it isn't set. Only the first %s is substituted. -# -if [ "${nfspath#*%s}" != "$nfspath" ]; then +if [ "${path#*%s}" != "$path" ]; then ip=$(ip -o -f inet addr show $netif) ip=${ip%%/*} ip=${ip##* } read node < /proc/sys/kernel/hostname [ "$node" = "(none)" ] && node=$ip - nfspath=${nfspath%%%s*}$node${nfspath#*%s} + path=${path%%%s*}$node${path#*%s} fi -# Look through the flags and see if any are overridden by the command line -# Append a , so we know we terminate -flags=${flags}, -while [ -n "$flags" ]; do - f=${flags%%,*} - flags=${flags#*,} - if [ -z "$f" ]; then - break - fi - if [ "$f" = "ro" -o "$f" = "rw" ]; then - nfsrw=$f - continue - fi - if [ "$f" = "lock" -o "$f" = "nolock" ]; then - nfslock=$f - continue - fi - nfsflags=${nfsflags+$nfsflags,}$f +# Look through the options and remove rw/locking options +OLDIFS=$IFS +IFS=, +for f in $options ; do + [ "$f" = "ro" -o "$f" = "rw" ] && nfsrw=$f && continue + [ "$f" = "lock" -o "$f" = "nolock" ] && nfslock=$f && continue + flags=${flags:+$flags,}$f done +IFS=$OLDIFS +options=$flags # Override rw/ro if set on cmdline getarg ro && nfsrw=ro getarg rw && nfsrw=rw -nfsflags=${nfsflags+$nfsflags,}${nfsrw} -# Load the modules so the filesystem type is there -incol2 /proc/filesystems nfs || modprobe nfs || exit 1 -incol2 /proc/filesystems nfs4 || modprobe nfs || exit 1 +# Default to ro if unset +[ -z "$nfsrw" ] && nfsrw=ro + +options=${options:+$options,}$nfsrw # Start rpcbind or rpcbind # FIXME occasionally saw 'rpcbind: fork failed: No such device' -- why? [ -x /sbin/portmap ] && [ -z "$(pidof portmap)" ] && portmap [ -x /sbin/rpcbind ] && [ -z "$(pidof rpcbind)" ] && rpcbind -if [ "$nfsver" = "nfs4" ]; then +if [ "$nfs" = "nfs4" ]; then # Start rpc.statd as mount won't let us use locks on a NFSv4 # filesystem without talking to it. NFSv4 does locks internally, # rpc.lockd isn't needed @@ -87,19 +147,17 @@ if [ "$nfsver" = "nfs4" ]; then # XXX really needed? Do we need non-root users before we start it in # XXX the real root image? - if [ -z "$(pidof rpc.idmapd)" ]; then - rpc.idmapd - fi + [ -z "$(pidof rpc.idmapd)" ] && rpc.idmapd # XXX Should we loop here? - exec mount -t nfs4 -o${nfsflags}${nfslock+,$nfslock} \ - $nfsserver:$nfspath $NEWROOT + exec mount -t nfs4 -o$options${nfslock+,$nfslock} \ + $server:$path $NEWROOT fi # NFSv{2,3} doesn't support using locks as it requires a helper to transfer # the rpcbind state to the new root -[ -z "$nfslock" -o "$nfslock" = "lock" ] && - echo "Warning: Locks unsupported on NFSv{2,3}, using nolock" 1>&2 +[ "$nfslock" = "lock" ] && \ + warn "Locks unsupported on NFSv{2,3}, using nolock" 1>&2 # XXX Should we loop here? -exec mount -t nfs -onolock,$nfsflags $nfsserver:$nfspath $NEWROOT +exec mount -t nfs -o$options${options:+,}nolock $server:$path $NEWROOT diff --git a/modules.d/95nfs/parse-nfsroot.sh b/modules.d/95nfs/parse-nfsroot.sh index 294ca28..d47be9e 100755 --- a/modules.d/95nfs/parse-nfsroot.sh +++ b/modules.d/95nfs/parse-nfsroot.sh @@ -1,55 +1,136 @@ -# We're 90-nfs.sh to catch root=/dev/nfs +#!/bin/sh # # Preferred format: # root=nfs[4]:[server:]path[:options] -# netroot=nfs[4]:[server:]path[:options] +# [root=*] netroot=nfs[4]:[server:]path[:options] +# +# Legacy formats: +# [net]root=[[/dev/]nfs[4]] nfsroot=[server:]path[,options] +# [net]root=[[/dev/]nfs[4]] nfsroot=[server:]path[:options] +# +# If the 'nfsroot' parameter is not given on the command line or is empty, +# the dhcp root-path is used as [server:]path[:options] or the default +# "/tftpboot/%s" will be used. # # If server is unspecified it will be pulled from one of the following # sources, in order: # static ip= option on kernel command line # DHCP next-server option # DHCP server-id option +# DHCP root-path option # -# Legacy formats: -# root=nfs[4] -# root=/dev/nfs[4] nfsroot=[server:]path[,options] +# NFSv4 is only used if explicitly requested; default is NFSv2 or NFSv3 +# depending on kernel configuration # -# Plain "root=nfs" interprets DHCP root-path option as [ip:]path[:options] -# -# NFSv4 is only used if explicitly listed; default is NFSv3 +# root= takes precedence over netroot= if root=nfs[...] # -case "$root" in - nfs|dhcp|'') - if getarg nfsroot= > /dev/null; then - root=nfs:$(getarg nfsroot=) - fi - ;; - nfs4) - if getarg nfsroot= > /dev/null; then - root=nfs4:$(getarg nfsroot=) - fi - ;; - /dev/nfs|/dev/nfs4) - if getarg nfsroot= > /dev/null; then - root=${root#/dev/}:$(getarg nfsroot=) - else - root=${root#/dev/} - fi - ;; +# Sadly there's no easy way to split ':' separated lines into variables +netroot_to_var() { + local v=${1}: + set -- + while [ -n "$v" ]; do + set -- "$@" "${v%%:*}" + v=${v#*:} + done + + unset nfs server path options + + nfs=$1 + # Ugly: Can't -z test #path after the case, since it might be allowed + # to be empty for root=nfs + case $# in + 0|1);; + 2) path=${2:-error};; + 3) + # This is ultra ugly. But we can't decide in which position path + # sits without checking if the string starts with '/' + case $2 in + /*) path=$2; options=$3;; + *) server=$2; path=${3:-error};; + esac + ;; + *) server=$2; path=${3:-error}; options=$4; + esac + + # Does it really start with '/'? + [ -n "${path%%/*}" ] && path="error"; + + #Fix kernel legacy style separating path and options with ',' + if [ "$path" != "${path#*,}" ] ; then + options=${path#*,} + path=${path%%,*} + fi +} + +#Don't continue if root is ok +[ -n "$rootok" ] && return + +# This script is sourced, so root should be set. But let's be paranoid +[ -z "$root" ] && root=$(getarg root=) +[ -z "$netroot" ] && netroot=$(getarg netroot=) +[ -z "$nfsroot" ] && nfsroot=$(getarg nfsroot=) + +# Root takes precedence over netroot +case "${root%%:*}" in + nfs|nfs4|/dev/nfs|/dev/nfs4) + if [ -n "$netroot" ] ; then + warn "root takes precedence over netroot. Ignoring netroot" + + fi + netroot=$root + ;; +esac + +# If it's not empty or nfs we don't continue +case "${netroot%%:*}" in + ''|nfs|nfs4|/dev/nfs|/dev/nfs4);; + *) return;; esac -if [ -z "$netroot" -a -n "$root" -a -z "${root%%nfs*}" ]; then - netroot="$root" - unset root +if [ -n "$nfsroot" ] ; then + [ -z "$netroot" ] && netroot=$root + + # @deprecated + warn "Argument nfsroot is deprecated and might be removed in a future release. See http://apps.sourceforge.net/trac/dracut/wiki/commandline for more information." + + case "$netroot" in + ''|nfs|nfs4|/dev/nfs|/dev/nfs4) netroot=${netroot:-nfs}:$nfsroot;; + *) die "Argument nfsroot only accepted for empty root= or root=[/dev/]nfs[4]" + esac fi -case "$netroot" in - nfs|nfs4|nfs:*|nfs4:*) - rootok=1 - if [ -n "$root" -a "$netroot" != "$root" ]; then - echo "WARNING: root= and netroot= do not match, ignoring root=" - fi - unset root - ;; +# If it's not nfs we don't continue +case "${netroot%%:*}" in + nfs|nfs4|/dev/nfs|/dev/nfs4);; + *) return;; esac + +# Check required arguments +netroot_to_var $netroot +[ "$path" = "error" ] && die "Argument nfsroot must contain a valid path!" + +# Set fstype, might help somewhere +fstype=${nfs#/dev/} + +# NFS actually supported? Some more uglyness here: nfs3 or nfs4 might not +# be in the module... +if ! incol2 /proc/filesystems $fstype ; then + modprobe nfs + incol2 /proc/filesystems $fstype || die "nfsroot type $fstype requested but kernel/initrd does not support nfs" +fi + +# Rewrite root so we don't have to parse this uglyness later on again +netroot="$fstype:$server:$path:$options" + +# If we don't have a server, we need dhcp +if [ -z "$server" ] ; then + NEEDDHCP="1" +fi; + +# Done, all good! +rootok=1 + +# Shut up init error check or make sure that block parser wont get +# confused by having /dev/nfs[4] +root="$fstype" diff --git a/test/TEST-20-NFS/test.sh b/test/TEST-20-NFS/test.sh index 00bdc0d..7dc3245 100755 --- a/test/TEST-20-NFS/test.sh +++ b/test/TEST-20-NFS/test.sh @@ -43,11 +43,11 @@ client_test() { fi $testdir/run-qemu -hda client.img -m 512M -nographic \ - -net nic,macaddr=$mac,model=e1000 \ - -net socket,mcast=230.0.0.1:1234 \ - -kernel /boot/vmlinuz-$KVERSION \ - -append "$cmdline $DEBUGFAIL ro quiet console=ttyS0,115200n81" \ - -initrd initramfs.testing + -net nic,macaddr=$mac,model=e1000 \ + -net socket,mcast=230.0.0.1:1234 \ + -kernel /boot/vmlinuz-$KVERSION \ + -append "$cmdline $DEBUGFAIL ro quiet console=ttyS0,115200n81" \ + -initrd initramfs.testing if [[ $? -ne 0 ]] || ! grep -m 1 -q nfs-OK client.img; then echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]" @@ -101,45 +101,49 @@ test_nfsv3() { # NFSv3: last octect starts at 0x00 and works up # NFSv4: last octect starts at 0x80 and works up + # This test must fail: dhcp root-path must have proto:... client_test "NFSv3 netroot=dhcp DHCP path only" 52:54:00:12:34:00 \ - "netroot=dhcp" 192.168.50.1 -wsize=4096 || return 1 + "netroot=dhcp" 192.168.50.1 -wsize=4096 && return 1 + # This test must fail: dhcp root-path must have proto:... client_test "NFSv3 root=dhcp DHCP path only" 52:54:00:12:34:00 \ - "root=dhcp" 192.168.50.1 -wsize=4096 || return 1 + "root=dhcp" 192.168.50.1 -wsize=4096 && return 1 client_test "NFSv3 root=nfs DHCP path only" 52:54:00:12:34:00 \ - "root=nfs" 192.168.50.1 -wsize=4096 || return 1 + "root=nfs" 192.168.50.1 -wsize=4096 || return 1 client_test "NFSv3 root=/dev/nfs DHCP path only" 52:54:00:12:34:00 \ - "root=/dev/nfs" 192.168.50.1 -wsize=4096 || return 1 + "root=/dev/nfs" 192.168.50.1 -wsize=4096 || return 1 + # This test must fail: dhcp root-path must have proto:... client_test "NFSv3 netroot=dhcp DHCP IP:path" 52:54:00:12:34:01 \ - "netroot=dhcp" 192.168.50.2 -wsize=4096 || return 1 + "netroot=dhcp" 192.168.50.2 -wsize=4096 && return 1 + # This test must fail: dhcp root-path must have proto:... client_test "NFSv3 root=dhcp DHCP IP:path" 52:54:00:12:34:01 \ - "root=dhcp" 192.168.50.2 -wsize=4096 || return 1 + "root=dhcp" 192.168.50.2 -wsize=4096 && return 1 client_test "NFSv3 root=nfs DHCP IP:path" 52:54:00:12:34:01 \ - "root=nfs" 192.168.50.2 -wsize=4096 || return 1 + "root=nfs" 192.168.50.2 -wsize=4096 || return 1 client_test "NFSv3 root=/dev/nfs DHCP IP:path" 52:54:00:12:34:01 \ - "root=/dev/nfs" 192.168.50.2 -wsize=4096 || return 1 + "root=/dev/nfs" 192.168.50.2 -wsize=4096 || return 1 client_test "NFSv3 root=dhcp DHCP proto:IP:path" 52:54:00:12:34:02 \ - "root=dhcp" 192.168.50.3 -wsize=4096 || return 1 + "root=dhcp" 192.168.50.3 -wsize=4096 || return 1 client_test "NFSv3 netroot=dhcp DHCP proto:IP:path:options" \ - 52:54:00:12:34:03 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1 + 52:54:00:12:34:03 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1 client_test "NFSv3 root=dhcp DHCP proto:IP:path:options" 52:54:00:12:34:03 \ - "root=dhcp" 192.168.50.3 wsize=4096 || return 1 + "root=dhcp" 192.168.50.3 wsize=4096 || return 1 client_test "NFSv3 netroot=nfs:..." 52:54:00:12:34:04 \ - "netroot=nfs:192.168.50.1:/nfs/client" 192.168.50.1 \ - -wsize=4096 || return 1 + "netroot=nfs:192.168.50.1:/nfs/client" 192.168.50.1 \ + -wsize=4096 || return 1 client_test "NFSv3 root=nfs:..." 52:54:00:12:34:04 \ - "root=nfs:192.168.50.1:/nfs/client" 192.168.50.1 -wsize=4096 || return 1 + "root=nfs:192.168.50.1:/nfs/client" 192.168.50.1 -wsize=4096 || return 1 client_test "NFSv3 nfsroot=/nfs/client" 52:54:00:12:34:04 \ "nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 || return 1 @@ -147,8 +151,9 @@ test_nfsv3() { client_test "NFSv3 nfsroot=192.168.50.2:/nfs/client" 52:54:00:12:34:04 \ "nfsroot=192.168.50.2:/nfs/client" 192.168.50.2 -wsize=4096 || return 1 + # This test must fail: root=dhcp must ignore other arguments client_test "NFSv3 root=dhcp nfsroot=/nfs/client" 52:54:00:12:34:04 \ - "root=dhcp nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 || return 1 + "root=dhcp nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 && return 1 client_test "NFSv3 root=nfs nfsroot=/nfs/client" 52:54:00:12:34:04 \ "root=nfs nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 || return 1 @@ -166,11 +171,13 @@ test_nfsv3() { 52:54:00:12:34:04 "root=nfs nfsroot=/nfs/client,wsize=4096" \ 192.168.50.1 wsize=4096 || return 1 - client_test "NFSv3 root=nfs DHCP path,options" \ - 52:54:00:12:34:05 "root=dhcp" 192.168.50.1 wsize=4096 || return 1 + # This test must fail: dhcp root-path must have proto:... + client_test "NFSv3 root=dhcp DHCP path,options" \ + 52:54:00:12:34:05 "root=dhcp" 192.168.50.1 wsize=4096 && return 1 + # This test must fail: dhcp root-path must have proto:... client_test "NFSv3 root=dhcp DHCP IP:path,options" \ - 52:54:00:12:34:06 "root=dhcp" 192.168.50.2 wsize=4096 || return 1 + 52:54:00:12:34:06 "root=dhcp" 192.168.50.2 wsize=4096 && return 1 client_test "NFSv3 root=dhcp DHCP proto:IP:path,options" \ 52:54:00:12:34:07 "root=dhcp" 192.168.50.3 wsize=4096 || return 1 @@ -182,34 +189,34 @@ test_nfsv4() { # switch_root client_test "NFSv4 netroot=nfs4 DHCP path only" 52:54:00:12:34:80 \ - "netroot=nfs4" 192.168.50.1 -wsize=4096 || return 1 - + "netroot=nfs4" 192.168.50.1 -wsize=4096 || return 1 + client_test "NFSv4 root=nfs4 DHCP path only" 52:54:00:12:34:80 \ - "root=nfs4" 192.168.50.1 -wsize=4096 || return 1 + "root=nfs4" 192.168.50.1 -wsize=4096 || return 1 client_test "NFSv4 root=/dev/nfs4 DHCP path only" 52:54:00:12:34:80 \ - "root=/dev/nfs4" 192.168.50.1 -wsize=4096 || return 1 + "root=/dev/nfs4" 192.168.50.1 -wsize=4096 || return 1 client_test "NFSv4 netroot=nfs4 DHCP IP:path" 52:54:00:12:34:81 \ - "netroot=nfs4" 192.168.50.2 -wsize=4096 || return 1 + "netroot=nfs4" 192.168.50.2 -wsize=4096 || return 1 client_test "NFSv4 root=nfs4 DHCP IP:path" 52:54:00:12:34:81 \ - "root=nfs4" 192.168.50.2 -wsize=4096 || return 1 + "root=nfs4" 192.168.50.2 -wsize=4096 || return 1 client_test "NFSv4 root=/dev/nfs4 DHCP IP:path" 52:54:00:12:34:81 \ - "root=/dev/nfs4" 192.168.50.2 -wsize=4096 || return 1 + "root=/dev/nfs4" 192.168.50.2 -wsize=4096 || return 1 client_test "NFSv4 netroot=dhcp DHCP proto:IP:path" 52:54:00:12:34:82 \ - "netroot=dhcp" 192.168.50.3 -wsize=4096 || return 1 + "netroot=dhcp" 192.168.50.3 -wsize=4096 || return 1 client_test "NFSv4 root=dhcp DHCP proto:IP:path" 52:54:00:12:34:82 \ - "root=dhcp" 192.168.50.3 -wsize=4096 || return 1 + "root=dhcp" 192.168.50.3 -wsize=4096 || return 1 client_test "NFSv4 netroot=dhcp DHCP proto:IP:path:options" \ - 52:54:00:12:34:83 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1 + 52:54:00:12:34:83 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1 client_test "NFSv4 root=dhcp DHCP proto:IP:path:options" 52:54:00:12:34:83 \ - "root=dhcp" 192.168.50.3 wsize=4096 || return 1 + "root=dhcp" 192.168.50.3 wsize=4096 || return 1 client_test "NFSv4 netroot=nfs4:..." 52:54:00:12:34:84 \ "netroot=nfs4:192.168.50.1:/client" 192.168.50.1 \ diff --git a/test/TEST-40-NBD/test.sh b/test/TEST-40-NBD/test.sh index e806d6d..8d98c08 100755 --- a/test/TEST-40-NBD/test.sh +++ b/test/TEST-40-NBD/test.sh @@ -92,47 +92,49 @@ test_run() { # The default is ext3,errors=continue so use that to determine # if our options were parsed and used - client_test "NBD root=nbd:IP:port" 52:54:00:12:34:00 \ - "root=nbd:192.168.50.1:2000" || return 1 + client_test "NBD root=nbd:IP:port" 52:54:00:12:34:00 \ + "root=nbd:192.168.50.1:2000" || return 1 - client_test "NBD root=nbd:IP:port:fstype" 52:54:00:12:34:00 \ - "root=nbd:192.168.50.1:2000:ext2" ext2 || return 1 + client_test "NBD root=nbd:IP:port:fstype" 52:54:00:12:34:00 \ + "root=nbd:192.168.50.1:2000:ext2" ext2 || return 1 - client_test "NBD root=nbd:IP:port::fsopts" 52:54:00:12:34:00 \ - "root=nbd:192.168.50.1:2000::errors=panic" \ - ext3 errors=panic || return 1 + client_test "NBD root=nbd:IP:port::fsopts" 52:54:00:12:34:00 \ + "root=nbd:192.168.50.1:2000::errors=panic" \ + ext3 errors=panic || return 1 - client_test "NBD root=nbd:IP:port:fstype:fsopts" 52:54:00:12:34:00 \ - "root=nbd:192.168.50.1:2000:ext2:errors=panic" \ - ext2 errors=panic || return 1 + client_test "NBD root=nbd:IP:port:fstype:fsopts" 52:54:00:12:34:00 \ + "root=nbd:192.168.50.1:2000:ext2:errors=panic" \ + ext2 errors=panic || return 1 - # There doesn't seem to be a good way to validate the NBD options, so - # just check that we don't screw up the other options + # There doesn't seem to be a good way to validate the NBD options, so + # just check that we don't screw up the other options - client_test "NBD root=nbd:IP:port:::NBD opts" 52:54:00:12:34:00 \ - "root=nbd:192.168.50.1:2000:::bs=2048" || return 1 + client_test "NBD root=nbd:IP:port:::NBD opts" 52:54:00:12:34:00 \ + "root=nbd:192.168.50.1:2000:::bs=2048" || return 1 - client_test "NBD root=nbd:IP:port:fstype::NBD opts" 52:54:00:12:34:00 \ - "root=nbd:192.168.50.1:2000:ext2::bs=2048" ext2 || return 1 + client_test "NBD root=nbd:IP:port:fstype::NBD opts" 52:54:00:12:34:00 \ + "root=nbd:192.168.50.1:2000:ext2::bs=2048" ext2 || return 1 - client_test "NBD root=nbd:IP:port:fstype:fsopts:NBD opts" \ - 52:54:00:12:34:00 \ - "root=nbd:192.168.50.1:2000:ext2:errors=panic:bs=2048" \ - ext2 errors=panic || return 1 + client_test "NBD root=nbd:IP:port:fstype:fsopts:NBD opts" \ + 52:54:00:12:34:00 \ + "root=nbd:192.168.50.1:2000:ext2:errors=panic:bs=2048" \ + ext2 errors=panic || return 1 - # Check legacy parsing + # Check legacy parsing - client_test "NBD root=nbd nbdroot=srv:port" 52:54:00:12:34:00 \ - "root=nbd nbdroot=192.168.50.1:2000" || return 1 + client_test "NBD root=nbd nbdroot=srv:port" 52:54:00:12:34:00 \ + "root=nbd nbdroot=192.168.50.1:2000" || return 1 - client_test "NBD root=dhcp nbdroot=srv:port" 52:54:00:12:34:00 \ - "root=dhcp nbdroot=192.168.50.1:2000" || return 1 + # This test must fail: root=dhcp ignores nbdroot + client_test "NBD root=dhcp nbdroot=srv:port" 52:54:00:12:34:00 \ + "root=dhcp nbdroot=192.168.50.1:2000" && return 1 - client_test "NBD root=nbd nbdroot=srv,port" 52:54:00:12:34:00 \ - "root=nbd nbdroot=192.168.50.1,2000" || return 1 + client_test "NBD root=nbd nbdroot=srv,port" 52:54:00:12:34:00 \ + "root=nbd nbdroot=192.168.50.1,2000" || return 1 - client_test "NBD root=dhcp nbdroot=srv,port" 52:54:00:12:34:00 \ - "root=dhcp nbdroot=192.168.50.1,2000" || return 1 + # This test must fail: root=dhcp ignores nbdroot + client_test "NBD root=dhcp nbdroot=srv,port" 52:54:00:12:34:00 \ + "root=dhcp nbdroot=192.168.50.1,2000" && return 1 # DHCP root-path parsing