[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#694668: A patch improve the performance of os-prober for systems with many partitions



Package: os-prober
Version: 1.56
Tags: patch


This bug is reported by one of our users, and also the patch is provided by him. And this is his comments on the provided patch:

Here's a patch.  The elapsed wall-clock times in seconds are:
                      os-prober   linux-boot-prober
   os-prober-1.56        12.9           3.6
   patched                5.9           0.4

where os-prober analyzes all 43 partitions, but linux-boot-prober only 1.
So that is a factor of 9X for linux-boot-prober, but only 2.2X for os-prober.
During os-prober, "vmstat 2" shows CPU time about:
   10%  user
    9%  system
   20%  idle
   60%  wait

and indeed the hardware LED for "disk active" is ON almost all the time.
I do not understand why the idle and wait times are not close to zero.

The internal documentation is lacking.  I had to deduce the strategies.

Depending on usage, linux-boot-prober could be added to the os-prober pipeline.
(I changed linux-boot-prober to read stdin instead of using $1.)
The overlap should be enough to hide the entire time for linux-boot-prober.
(That would give 7X faster for the whole task as seen by user of anaconda.)
Also, there is no comment about the reason for using "blockdev --setro"
when "mount -ro" is used already.

My box has 3GHz dual CPU, fedora-18-Beta-RC1, and these disks:
   4  7200rpm SATA II drives; 2 GPT, 2 MSDOS
  32  linux root partitions
   5  data (non-root) partitions
   3  swap partitions (one per drive except Windows)
   2  not-formatted partitions
   1  Windows partition

Overview of significant changes in the patch:
1. Factor out thouands of invocations of 'loggger' into a single process
   which reads stdin.
2. Change strategy from serial tests to a shell pipeline where
   success goes "out the side" while failure "falls through" to next
   stage in pipeline.
3. Avoid 'grep' and 'sed' in favor of shell string processing, "set --"
   under appropriate "IFS=", and 'eval'.
4. Avoid logging the whole contents of grub.cfg.  Mine has 1955 lines,
   and anyway the patched logging would suffer rate-limiting by syslog.
5. Use the -a (AND) operator to '[' (test) instead of multiple tests
   with '&&'.

Because of its use by debian-installer, os-prober wants 'dash' as shell,
and not 'bash', This is unfortunate because bash has '=~' for string maching,
"$(<" instead of "$(cat", and arrays.  Dash can use
"case "$param" in; pattern) ... ;; ... esac" for most simple uses of
" [[ "$param" =~ pattern ]] ", but somehow I find 'case' cumbersome.
So I use "_tmp="${param##pattern}"; if [ ${#_tmp} -ne ${#param} ]"
(etc.) to detect a match by difference in length.

diff --git a/common.sh b/common.sh
index 82d019e..3c9b0cf 100644
--- a/common.sh
+++ b/common.sh
@@ -5,10 +5,9 @@ newns () {
 cleanup_tmpdir=false
 cleanup_ro_partitions=
 cleanup () {
-  local partition
-  for partition in $cleanup_ro_partitions; do
-    blockdev --setrw "$partition"
-  done
+  if [ "$cleanup_ro_partitions" ]; then
+    blockdev --setrw $cleanup_ro_partitions
+  fi
   if $cleanup_tmpdir; then
     rm -rf "$OS_PROBER_TMP"
   fi
@@ -57,16 +56,75 @@ progname=
 cache_progname() {
   case $progname in
     '')
-      progname="$(basename "$0")"
+      progname="${0##*/}"
       ;;
   esac
 }
 
-log() {
-  cache_progname
-  logger -t "$progname" "$@"
+path_lookup() {
+    local _t
+    unset $1  # default to failure
+    if _t=$(type "$1" 2>/dev/null); then
+        set -- $_t  # "app is /path/to/app"
+        eval $1=\"$3\"
+    fi
+}
+
+strmid_extract() {  # str left right;  let str= a $2 b $3 c
+    local _b3c _b
+    eval '_b3c="${1#*'\\"$2"'}"'
+    eval '_b="${_b3c%'\\"$3"'*}"'
+    if [ "$_b3c" != "$1" -a "$_b" != "$_b3c" ]; then
+        # both prefix and suffix were removed
+        value="$_b"
+    else
+        value=""    # one was missing
+    fi
+}
+
+strmid_delete() {  # str left right;  let str= a $2 b $3 c
+    local _a _c
+    eval '_a="${1%%'\\"$2"'*}"'
+    eval '_c="${1##*'\\"$3"'}"'
+    if [ ${#1} -ge $(( ${#_a} + 2 + ${#_c} )) ]; then
+        # there was a middle
+        value="$_a$_c"
+    else
+        value="$1"  # one was missing
+    fi
+}
+
+strchar_delete() {  # str char
+    _IFS="$IFS"
+    IFS="$2"; set -- $1
+    IFS="";   value="$*"
+    IFS="$_IFS"
+}
+
+strcat_sep_split() {  # sep split str
+    local _sep _split
+    _sep="$1"; _split="$2"; shift 2
+    _IFS="$IFS"
+    IFS="$_split"; set -- $1  # adjacent splitters give only one cut
+    IFS="$_sep";   value="$*"
+    IFS="$_IFS"
+}
+
+# Single-quote "'" cannot be single quoted, because backslash does not escape.
+_PUNCT="'"'!"#$%&()*+,-./:;<=>?@[\]^`{|}~'  # non-whitespace non-identifier
+
+str_tame() {  # make $1 into an identifier
+    strcat_sep_split '_' "$_PUNCT" "$1"  # result is $value 
 }
 
+# fd_logger: bind value now, possibly after assigning default. 
+eval '
+  log() {
+    cache_progname
+    echo "$progname: $@"  1>&'${fd_logger:=9}'
+  }
+'
+
 error() {
   log "error: $@"
 }
@@ -79,10 +137,14 @@ debug() {
   log "debug: $@"
 }
 
-result () {
-  log "result:" "$@"
-  echo "$@"
-}
+
+# fd_result: bind value now, possibly after assigning default.
+eval '
+  result() {
+    log "result:" "$@"
+    echo "$@"  1>&'${fd_result:=1}'
+  }
+'
 
 # shim to make it easier to use os-prober outside d-i
 if ! type mapdevfs >/dev/null 2>&1; then
@@ -106,21 +168,20 @@ item_in_dir () {
 # We can't always tell the filesystem type up front, but if we have the
 # information then we should use it. Note that we can't use block-attr here
 # as it's only available in udebs.
-fs_type () {
-	if (export PATH="/lib/udev:$PATH"; type vol_id) >/dev/null 2>&1; then
-		PATH="/lib/udev:$PATH" vol_id --type "$1" 2>/dev/null
-	elif type blkid >/dev/null 2>&1; then
-		blkid -o value -s TYPE "$1" 2>/dev/null
-	else
-		return 0
-	fi
+define__fs_type() {  # needed so that "set --" does not change $1 of main program!
+    PATH="/lib/udev:$PATH" path_lookup vol_id; if [ "$vol_id" ]; then
+        eval 'fs_type() { '$vol_id' --type "$1" 2>/dev/null; }'
+    else path_lookup blkid; if [ "$blkid" ]; then
+        eval 'fs_type() { '$blkid' -o value -s TYPE "$1" 2>/dev/null; }'
+    else
+        fs_type() { return 0; }
+    fi; fi
 }
+define__fs_type
 
 parse_proc_mounts () {
 	while read -r line; do
-		set -f
-		set -- $line
-		set +f
+		set -f; set -- $line; set +f
 		printf '%s %s %s\n' "$(mapdevfs "$1")" "$2" "$3"
 	done
 }
@@ -132,56 +193,56 @@ parsefstab () {
 				:	
 			;;
 			*)
-				set -f
-				set -- $line
-				set +f
+				set -f; set -- $line; set +f
 				printf '%s %s %s\n' "$1" "$2" "$3"
 			;;
 		esac
 	done
 }
 
+# Change encoded <tab><newline><space><backslash> back to actual characters.
 unescape_mount () {
 	printf %s "$1" | \
 		sed 's/\\011/	/g; s/\\012/\n/g; s/\\040/ /g; s/\\134/\\/g'
 }
 
-ro_partition () {
-	if type blockdev >/dev/null 2>&1 && \
-	   [ "$(blockdev --getro "$1")" = 0 ] && \
-	   blockdev --setro "$1"; then
-		cleanup_ro_partitions="${cleanup_ro_partitions:+$cleanup_ro_partitions }$1"
+define__ro_partition() {
+    path_lookup blockdev; if [ "$blockdev" ]; then eval '
+        ro_partition() {
+	    if [ "$('$blockdev' --getro "$1")" = 0 ] \
+            &&      '$blockdev' --setro "$1"; then
+		cleanup_ro_partitions="$cleanup_ro_partitions $1"
 		trap cleanup EXIT HUP INT QUIT TERM
-	fi
-}
-
-find_label () {
-	local output
-	if type blkid >/dev/null 2>&1; then
-		# Hopefully everyone has blkid by now
-		output="$(blkid -o device -t LABEL="$1")" || return 1
-		echo "$output" | head -n1
-	elif [ -h "/dev/disk/by-label/$1" ]; then
-		# Last-ditch fallback
-		readlink -f "/dev/disk/by-label/$1"
-	else
-		return 1
-	fi
+	    fi
+        }'
+    else
+        ro_partition() { :; }  # ignore, for benefit of "set -e"
+    fi
 }
+define__ro_partition
 
-find_uuid () {
-	local output
-	if type blkid >/dev/null 2>&1; then
-		# Hopefully everyone has blkid by now
-		output="$(blkid -o device -t UUID="$1")" || return 1
-		echo "$output" | head -n1
-	elif [ -h "/dev/disk/by-uuid/$1" ]; then
-		# Last-ditch fallback
-		readlink -f "/dev/disk/by-uuid/$1"
-	else
-		return 1
-	fi
+define__find__kind() {
+    local name="$1"
+    local NAME="$2"
+    path_lookup blkid; if [ "$blkid" ]; then eval '
+        find_'$name'() {
+            local output
+            output="$('$blkid' -o device -t '$NAME'="$1")" || return 1
+            echo "$output" | head -n1
+        }'
+    else eval '
+        find_'$name'() {
+            if [ -h "/dev/disk/by-'$name'/$1" ]; then
+                readlink -f "/dev/disk/by-'$name'/$1"
+            else
+                return 1
+            fi
+        }'
+    fi
 }
+define__find__kind label LABEL  # find_label()
+define__find__kind uuid  UUID   # find_uuid()
+set +x
 
 # Sets $mountboot as output variable.  (We do this rather than requiring a
 # subshell so that we can run ro_partition without the cleanup trap firing
@@ -196,9 +257,7 @@ linux_mount_boot () {
 		# Try to mount any /boot partition.
 		bootmnt=$(parsefstab < "$tmpmnt/etc/fstab" | grep " /boot ") || true
 		if [ -n "$bootmnt" ]; then
-			set -f
-			set -- $bootmnt
-			set +f
+			set -f; set -- $bootmnt; set +f
 			boottomnt=""
 
 			# Try to map labels and UUIDs ourselves if possible,
@@ -283,3 +342,15 @@ linux_mount_boot () {
 
 	mountboot="$bootpart $mounted"
 }
+
+linux_unmount_boot() {
+	mpoint="$1"  um_root="$2"  um_boot="$3"
+	if [ 1 == "$um_boot" ]; then
+		umount "$mpoint/boot"
+		rmdir  "$mpoint/boot" 2>/dev/null || true
+	fi
+	if [ 1 == "$um_root" ]; then
+		umount "$mpoint"
+		rmdir  "$mpoint"
+	fi
+}
diff --git a/linux-boot-prober b/linux-boot-prober
index ee8f2b9..4298c1b 100755
--- a/linux-boot-prober
+++ b/linux-boot-prober
@@ -1,14 +1,50 @@
 #!/bin/sh
-. /usr/share/os-prober/common.sh
-
 set -e
 
+# $1: optional alternate root for scripts in this package
+# (for testing without install, etc.)
+# Use the default directories if $1 is not supplied.
+export    execpfx="${1:-/usr/share/os-prober}"
+export libexecpfx="${1:-/usr/libexec}/linux-boot-probes"
+
+# dash shell does not have "{varname}>&1" feature that bash shell has
+# for auto-assignment of new filedescriptors.
+# It is cumbersome to write the 'eval' to use our own variables.
+# Therefore use fixed numbers.
+export fd_result=3  # file descriptor for external results
+export fd_logger=9  # file descriptor for input to logger
+
+. $execpfx/common.sh  # note: binds fd_result and fd_logger
+
 newns "$@"
 require_tmpdir
 
+pipe_mounted=""
+for test in $libexecpfx/mounted/* \
+            $libexecpfx/mounted/x86/* \
+            $libexecpfx/mounted/common/*  # last two lines: test without install
+do
+    if [ -r "$test" -a -x "$test" -a ! -d "$test" ]; then
+        pipe_mounted="$pipe_mounted|$test"
+    fi
+done
+pipe_mounted="${pipe_mounted#|}"
+# echo pipe_mounted= "$pipe_mounted" 1>&2
+
+pipe_not_yet=""
+for test in $libexecpfx/*; do
+    if [ -r "$test" -a -x "$test" -a ! -d "$test" ]; then
+        pipe_not_yet="$pipe_not_yet|$test"
+    fi
+done
+pipe_not_yet="${pipe_not_yet#|}"
+# echo pipe_not_yet= "$pipe_not_yet" 1>&2
+
 grep "^/dev/" /proc/mounts | parse_proc_mounts >"$OS_PROBER_TMP/mounted-map" || true
 
-partition="$1"
+
+# READ partition FROM STDIN (THIS IS A CHANGE FROM PREVIOUS USAGE.)
+( ( ( while read partition; do
 
 if [ -z "$partition" ]; then
 	echo "usage: linux-boot-prober partition" >&2
@@ -21,41 +57,31 @@ if ! mapped="$(mapdevfs "$partition")"; then
 fi
 
 if ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map"; then
-	for test in /usr/libexec/linux-boot-probes/*; do
-		debug "running $test"
-		if [ -x $test ] && [ -f $test ]; then
-			if $test "$partition"; then
-				debug "linux detected by $test"
-				break
-			fi
-		fi
-	done
+	echo "$partition"  # pipe_not_yet
 else
 	mpoint=$(grep "^$mapped " "$OS_PROBER_TMP/mounted-map" | head -n1 | cut -d " " -f 2)
 	mpoint="$(unescape_mount "$mpoint")"
-	if [ "$mpoint" != "/target/boot" ] && [ "$mpoint" != "/target" ] && [ "$mpoint" != "/" ]; then
+	if [ "$mpoint" != "/target/boot" \
+	  -a "$mpoint" != "/target" \
+	  -a "$mpoint" != "/" ]; then
 		type=$(grep "^$mapped " "$OS_PROBER_TMP/mounted-map" | head -n1 | cut -d " " -f 3)
 		if ! grep -q " $mpoint/boot " "$OS_PROBER_TMP/mounted-map"; then
-			linux_mount_boot "$partition" "$mpoint"
+			linux_mount_boot "$partition" "$mpoint"  # mountboot="$bootpart $mounted"
 			bootpart="${mountboot%% *}"
 			bootmounted="${mountboot#* }"
 		else
 			bootpart="$partition"
 			bootmounted=0
 		fi
-		for test in /usr/libexec/linux-boot-probes/mounted/*; do
-			if [ -f $test ] && [ -x $test ]; then
-				debug "running $test on mounted $partition"
-				if $test "$partition" "$bootpart" "$mpoint" "$type"; then
-					debug "$test succeeded"
-					break
-				fi
-			fi
-		done
-		if [ "$bootmounted" = 1 ]; then
-			if ! umount "$mpoint/boot"; then
-				warn "failed to umount $mpoint/boot"
-			fi
-		fi
+		echo "$partition $bootpart $mpoint $type 0 $bootmounted"  1>&8  # pipe_mounted
 	fi
 fi
+
+done  \
+       | eval "${pipe_not_yet}"
+) 8>&1 | eval "${pipe_mounted}"  \
+       |  while read line; do echo "\?unknown $line"; done  1>&2
+) 9>&1 | logger 1>&-  # fd_logger
+) 3>&1  # fd_result
+
+# EOF linux-boot-prober
diff --git a/linux-boot-probes/common/50mounted-tests b/linux-boot-probes/common/50mounted-tests
index b649663..481255f 100755
--- a/linux-boot-probes/common/50mounted-tests
+++ b/linux-boot-probes/common/50mounted-tests
@@ -3,35 +3,45 @@
 . /usr/share/os-prober/common.sh
 set -e
 
-partition="$1"
+tmpmntdir=/var/lib/os-prober/mount
+mkdir  "$tmpmntdir" >/dev/null 2>&1 || true
+(cd $tmpmntdir
+    umount -f lbp* >/dev/null 2>&1 || true
+    rmdir     lbp* >/dev/null 2>&1 || true
+)
 
+while read partition; do
+
+tmpmnt=$tmpmntdir/lbp$(( mount_count+=1 ))
+mkdir $tmpmnt
+
+xtra_types=""
 types="$(fs_type "$partition")" || types=NOT-DETECTED
-if [ "$types" = NOT-DETECTED ]; then
+case "$types" in
+    NOT-DETECTED)
 	debug "$1 type not recognised; skipping"
-	exit 0
-elif [ "$types" = swap ]; then
+	continue ;;
+    swap)
 	debug "$1 is a swap partition; skipping"
-	exit 0
-elif [ "$types" = crypto_LUKS ]; then
+	continue ;;
+    crypto_LUKS)
 	debug "$1 is a LUKS partition; skipping"
-	exit 0
-elif [ "$types" = ntfs ]; then
+	continue ;;
+    ntfs)
 	if type ntfs-3g >/dev/null 2>&1; then
-		types='ntfs-3g ntfs'
-	fi
-elif [ -z "$types" ]; then
+		xtra_types='ntfs-3g ntfs'  
+	fi ;;
+    ?*)  # non-null
+	xtra_types="$types" ;;  # guess this one first
+    '')  # null
 	if type cryptsetup >/dev/null 2>&1 && \
 	   cryptsetup luksDump "$partition" >/dev/null 2>&1; then
 		debug "$1 is a LUKS partition; skipping"
-		exit 0
+		continue
 	fi
 	types="$(grep -v nodev /proc/filesystems)"
-fi
-
-tmpmnt=/var/lib/os-prober/mount
-if [ ! -d "$tmpmnt" ]; then
-	mkdir "$tmpmnt"
-fi
+	;;
+esac
 
 mounted=
 if type grub-mount >/dev/null 2>&1 && \
@@ -51,32 +61,13 @@ else
 fi
 
 if [ "$mounted" ]; then
-	linux_mount_boot "$partition" "$tmpmnt"
+	linux_mount_boot "$partition" "$tmpmnt"  # mountboot="$bootpart $mounted"
 	bootpart="${mountboot%% *}"
 	mounted="${mountboot#* }"
 
-	for test in /usr/libexec/linux-boot-probes/mounted/*; do
-		if [ -f "$test" ] && [ -x "$test" ]; then
-			debug "running $test $partition $bootpart $tmpmnt $type"
-			if $test "$partition" "$bootpart" "$tmpmnt" "$type"; then
-				debug "$test succeeded"
-				umount "$tmpmnt/boot" 2>/dev/null || true 	
-				if ! umount "$tmpmnt"; then
-					warn "failed to umount $tmpmnt"
-				fi
-				rmdir "$tmpmnt" || true
-				exit 0
-			fi
-		fi
-	done
-
-	umount "$tmpmnt/boot" 2>/dev/null || true 	
-	if ! umount "$tmpmnt"; then
-		warn "failed to umount $tmpmnt"
-	fi
+	echo "$partition $bootpart $tmpmnt $type 1 $mounted"  # 1: must unmount
+else
+	rmdir "$tmpmnt" || :
 fi
 
-rmdir "$tmpmnt" || true
-
-# No tests found anything.
-exit 1
+done
diff --git a/linux-boot-probes/mounted/common/40grub2 b/linux-boot-probes/mounted/common/40grub2
index 885614e..49acfd5 100755
--- a/linux-boot-probes/mounted/common/40grub2
+++ b/linux-boot-probes/mounted/common/40grub2
@@ -1,19 +1,12 @@
 #!/bin/sh
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 set -e
 
-partition="$1"
-bootpart="$2"
-mpoint="$3"
-type="$4"
-
-found_item=0
-
 entry_result () {
 	if [ "$ignore_item" = 0 ] && \
 	   [ -n "$kernel" ] && \
 	   [ -e "$mpoint/$kernel" ]; then
-		result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"
+		result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"  # success
 		found_item=1
 	fi
 	kernel=""
@@ -23,6 +16,11 @@ entry_result () {
 	ignore_item=0
 }
 
+parse_done () {
+    debug "${0##*/}: $parse_part: $lineno lines from $parse_file"
+    debug "${0##*/}: $parse_part: $on_dev \"(on /dev/...)\" were ignored"
+}
+
 parse_grub_menu () {
 	mpoint="$1"
 	rootpart="$2"
@@ -33,78 +31,158 @@ parse_grub_menu () {
 	initrd=""
 	title=""
 	ignore_item=0
+	lineno=0
+	on_dev=0
+	local OLDIFS="$IFS"
+	local _t
 
 	while read line; do
-		debug "parsing: $line"
-		set -f
-		set -- $line
-		set +f
+		(( ++lineno ))
+		# debug "parsing: $line"
+		set -f; set -- $line; set +f  # apply IFS; do not expand filenames
 		case "$1" in
-			menuentry)
-				entry_result
-				shift 1
-				# The double-quoted string is the title.
-				# Make sure to look at the text of the line
-				# before 'set' mangled it.
-				title="$(echo "$line" | sed -n 's/[^"]*"\(.*\)".*/\1/p' | sed 's/://g')"
-				if [ -z "$title" ]; then
-					# ... or single-quoted?  Be careful
-					# to handle constructions like
-					# 'foo'\''bar' (which expands to
-					# foo'bar, as in shell), and to
-					# handle multiple single-quoted
-					# strings on the same line.
-					title="$(echo "$line" | sed -n "s/[^']*'\(\([^']\|'\\\\''\)*\)'.*/\1/p" | sed "s/'\\\\''/'/; s/://g")"
-				fi
-				if [ -z "$title" ]; then
-					ignore_item=1
-				elif echo "$title" | grep -q '(on /dev/[^)]*)$'; then
-					log "Skipping entry '$title':"
-					log "appears to be an automatic reference taken from another menu.lst"
-					ignore_item=1
-				fi
+		menuentry)
+			# In each case we remove  :  because we use a colon
+			# to separate fields in the output.
+			entry_result
+			shift 1
+
+# Shell should provide a "tokenize" operator much like 'set'.  The output
+# for a literal string would be a prefix of '"', followed by the value.
+# The difficulty is handling escapes (backslashes).
+#
+# We cannot use 'eval' to tokenize, then take the second token:
+#    set -f; eval 'IFS="" set -- '"${line}"; set +f  # IFS: split no more!
+#    echo $2  # the token after the key word
+# because 'eval' detects syntax errors.  (Knowing where the quoting ends
+# is equivalent to lexing the string in the first place.)
+#
+# However, we can try the easy case first, before resorting to 'sed'.
+
+			case "$1" in
+			\"*) IFS='\'; set -f; set -- $line; set +f  # detect backslash
+			    IFS="$OLDIFS"
+			    if (( 1 == $# )); then  # no backslash
+			        IFS='"'; set -f; set -- $line; set +f  # split at double quote
+			        IFS="$OLDIFS"
+			        strchar_delete "$2" ':'; title="$value"
+			    else  # the hard case
+			        title="$(echo "$line" |
+			        $sed -n 's/[^"]*"\(\(\\[$`"\\]\|[^"]\)*\)".*/\1/p' |
+# The $sed above extracts the (longest) left-most double-quoted string, if any.
+# The outer  \(    \)  delimit what will be remembered for  \1 .
+# The inner  \( \| \)*  is a repeated alternation of two choices.
+# The first choice is backslash followed by dollar, backtick, double quote,
+# or backslash; those are the characters for which backslash retains its
+# meaning as an escape character within double quotes.
+# The second choice is any character other than a double quote.
+# A bare double quote would be the terminator.
+			        $sed 's/\\\([$`"\\]\)/\1/g; s/://g' )"
+# The $sed above evaluates the double quoting by processing the backslash
+# escapes which are legal within double quotes.
+			    fi
 			;;
-			linux)
-				# Hack alert: sed off any (hdn,n) but
-				# assume the kernel is on the same
-				# partition.
-				kernel="$(echo "$2" | sed 's/(.*)//')"
-				shift 2
-				parameters="$@"
-				# Systems with a separate /boot will not have
-				# the path to the kernel in grub.cfg.
-				if [ "$partition" != "$bootpart" ]; then
-					kernel="/boot$kernel"
-				fi
+			\'*) IFS='\';  set -f; set -- $line; set +f  # detect backslash
+			    IFS="$OLDIFS"
+			    if (( 1 == $# )); then  # no backslash
+			        IFS="'"; set -f; set -- $line; set +f  # split at single quote
+			        IFS="$OLDIFS"
+			        strchar_delete "$2" ':'; title="$value"
+			    else  # the hard case
+			        title="$(echo "$line" |
+			        $sed -n "s/[^']*'\(\([^']\|'\(\\\\'\)*'\)*\)'.*/\1/p" |
+# The $sed above extracts the (longest) left-most single-quoted string, if any.
+# The outer   \(    \)  delimit what will be remembered for  \1 .
+# The middle  \( \| \)*  is a repeated alternation of two choices.
+# The first choice is any character other than single quote.
+# The second choice is two single quotes which surround any number of  \' ,
+# which reads as that number of embedded single quotes.  'a'\'\''b' => "a''b"
+			        $sed "s/\\\\'/'/g; s/'\\('*\\)'/\\1/g; s/://g" )"
+# The $sed above evaluates the single quoting.
+# Any  \'  becomes that single quote.
+# Then a run of at least two single quotes becomes the interior of that run.
+			    fi
 			;;
-			initrd)
-				initrd="$(echo "$2" | sed 's/(.*)//')"
-				# Initrd same.
-				if [ "$partition" != "$bootpart" ]; then
-					initrd="/boot$initrd"
-				fi
-			;;
-			"}")
-				entry_result
+			*) strchr_delete "$1" ':'; title="$value"
 			;;
+			esac
+
+			_t="${title%(on /dev/[!)]*)}"  # speculative
+			if [ -z "$title" ]; then
+				ignore_item=1
+			elif [ ${#_t} -ne ${#title} ]; then  # was "(on /dev/...)"
+				if (( ++on_dev <= 2 )); then
+log "Skipping entry '$title':"
+log "appears to be an automatic reference taken from another menu.lst"
+					if (( on_dev == 2)); then
+log "Further \"(on /dev/...)\" entries will be counted but not logged."
+					fi
+				fi
+				ignore_item=1
+			fi
+		;;
+		linux)
+			# Hack alert: sed off any (hdn,n) but
+			# assume the kernel is on the same
+			# partition.
+			#kernel="$(echo "$2" | $sed 's/(.*)//')"
+			strmid_delete  "$2"  "("  ")";  kernel="$value"
+			shift 2
+			parameters="$@"
+			# Systems with a separate /boot will not have
+			# the path to the kernel in grub.cfg.
+			if [ "$partition" != "$bootpart" ]; then
+				kernel="/boot$kernel"
+			fi
+		;;
+		initrd)
+			#initrd="$(echo "$2" | $sed 's/(.*)//')"
+			strmid_delete  "$2"  "("  ")";  initrd="$value"
+			# Initrd same.
+			if [ "$partition" != "$bootpart" ]; then
+				initrd="/boot$initrd"
+			fi
+		;;
+		"}")
+			entry_result
+		;;
 		esac
 	done
 
 	entry_result
 }
 
-if [ -e "$mpoint/boot/grub/grub.cfg" ] && \
-   ([ ! -e "$mpoint/boot/grub/menu.lst" ] || \
-    [ "$mpoint/boot/grub/grub.cfg" -nt "$mpoint/boot/grub/menu.lst" ]); then
-	debug "parsing grub.cfg"
-	parse_grub_menu "$mpoint" "$partition" "$bootpart" < "$mpoint/boot/grub/grub.cfg"
-elif [ -e "$mpoint/boot/grub2/grub.cfg" ]; then
-	debug "parsing grub.cfg"
-	parse_grub_menu "$mpoint" "$partition" "$bootpart" < "$mpoint/boot/grub2/grub.cfg"
+path_lookup sed  # find 'sed' in $PATH; set $sed
+export sed  # for sub-shells
+
+trap parse_done EXIT HUP INT QUIT TERM
+
+while read partition bootpart mpoint type um_root um_boot; do
+# echo $0: $read $partition $bootpart $mpoint $type $um_root $um_boot 1>&2
+
+found_item=0
+lineno=0
+
+parse_part="$partition"
+parse_file="$mpoint/boot/grub/menu.lst"
+if [   -e  "$mpoint/boot/grub/grub.cfg" ]  \
+&& [ ! -e  "$mpoint/boot/grub/menu.lst"  \
+       -o  "$mpoint/boot/grub/grub.cfg"  \
+       -nt "$mpoint/boot/grub/menu.lst" ]; then
+	parse_file="$mpoint/boot/grub/grub.cfg"
+elif [ -e          "$mpoint/boot/grub2/grub.cfg" ]; then
+	parse_file="$mpoint/boot/grub2/grub.cfg"
+fi
+if [ "$parse_file" ]; then
+	debug "${0##*/}: $parse_part: parsing $parse_file"
+	parse_grub_menu "$mpoint" "$partition" "$bootpart" < "$parse_file"
+	parse_done
 fi
 
 if [ "$found_item" = 0 ]; then
-	exit 1
+	echo "$partition $bootpart $mpoint $type $um_root $um_boot"  # failure
 else
-	exit 0
+	linux_unmount_boot "$mpoint" "$um_root" "$um_boot"
 fi
+
+done
diff --git a/linux-boot-probes/mounted/common/90fallback b/linux-boot-probes/mounted/common/90fallback
index 9ff78e1..dbc9700 100755
--- a/linux-boot-probes/mounted/common/90fallback
+++ b/linux-boot-probes/mounted/common/90fallback
@@ -1,13 +1,11 @@
 #!/bin/sh
 # Fallback in case nothing else works. Look for vmlinu[xz] file in root and
 # /boot, see if there is a matching initrd, and wing it.
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 set -e
 
-partition="$1"
-bootpart="$2"
-mpoint="$3"
-type="$4"
+while read partition bootpart mpoint type um_root um_boot; do
+# echo $0: $read $partition $bootpart $mpoint $type $um_root $um_boot 1>&2
 
 mappedpartition=$(mapdevfs "$partition" 2>/dev/null) || mappedpartition="$partition"
 
@@ -21,7 +19,7 @@ for kernpat in /vmlinuz /vmlinux /boot/vmlinuz /boot/vmlinux "/boot/vmlinuz*" \
 	fi
 	for kernfile in $(eval ls "$mpoint$kernpat" 2>/dev/null); do
 		kernbasefile=$(echo "$kernfile" | sed "s!^$mpoint!!")
-		if [ -f "$kernfile" ] && [ ! -L "$kernfile" ]; then
+		if [ -f "$kernfile" -a ! -L "$kernfile" ]; then
 			initrdname=$(echo "$kernfile" | sed "s/vmlinu[zx]/initrd\*/")
 			# Yellow Dog Linux appends .img to it.
 			initrdname1="${initrdname}.img"
@@ -36,18 +34,28 @@ for kernpat in /vmlinuz /vmlinux /boot/vmlinuz /boot/vmlinux "/boot/vmlinuz*" \
 			initrdname4=$(echo "$kernfile" | sed "s/kernel/initramfs\*/")
 			foundinitrd=0
 			for initrd in $(eval ls "$initrdname" "$initrdname1" "$initrdname2" "$initrdname3" "$initrdname4" 2>/dev/null); do
-				if [ "$initrd" != "$kernfile" ] && [ -f "$initrd" ] && [ ! -L "$initrd" ]; then
+				if [ "$initrd" != "$kernfile" -a -f "$initrd" -a ! -L "$initrd" ]; then
 					initrd=$(echo "$initrd" | sed "s!^$mpoint!!")
-					result "$partition:$kernbootpart::$kernbasefile:$initrd:root=$mappedpartition"
+result "$partition:$kernbootpart::$kernbasefile:$initrd:root=$mappedpartition"  # success
 					exitcode=0
 					foundinitrd=1
 				fi
 			done
 			if [ "$foundinitrd" = 0 ]; then
-				result "$partition:$kernbootpart::$kernbasefile::root=$mappedpartition"
+result "$partition:$kernbootpart::$kernbasefile::root=$mappedpartition"  # success
 				exitcode=0
 			fi
 		fi
 	done
 done
-exit "$exitcode"
+
+if [ 0 != "$exitcode" ]; then
+	echo "$partition $bootpart $mpoint $type"  # failure
+fi
+if [ 0 != "$exitcode" ]; then
+	echo "$partition $bootpart $mpoint $type $um_root $um_boot"  # failure
+else
+	linux_unmount_boot "$mpoint" "$um_root" "$um_boot"
+fi
+
+done
diff --git a/linux-boot-probes/mounted/x86/40grub b/linux-boot-probes/mounted/x86/40grub
index 08f6605..8d501c7 100755
--- a/linux-boot-probes/mounted/x86/40grub
+++ b/linux-boot-probes/mounted/x86/40grub
@@ -1,19 +1,11 @@
 #!/bin/sh
-. /usr/share/os-prober/common.sh
-set -e
-
-partition="$1"
-bootpart="$2"
-mpoint="$3"
-type="$4"
 
-found_item=0
+. $execpfx/common.sh
+set -e
 
 entry_result () {
-	if [ "$ignore_item" = 0 ] && \
-	   [ -n "$kernel" ] && \
-	   [ -e "$mpoint/$kernel" ]; then
-		result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"
+	if [ "$ignore_item" = 0 -a -n "$kernel" -a -e "$mpoint/$kernel" ]; then
+		result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"  # success
 		found_item=1
 	fi
 	kernel=""
@@ -23,6 +15,10 @@ entry_result () {
 	ignore_item=0
 }
 
+parse_done () {
+    debug "${0##*/}: $parse_part: $lineno lines from $grubconf"
+}
+
 parse_grub_menu () {
 	mpoint="$1"
 	rootpart="$2"
@@ -35,16 +31,18 @@ parse_grub_menu () {
 	ignore_item=0
 
 	while read line; do
+		(( ++lineno ))
 		#debug "parsing: $line"
-		set -f
-		set -- $line
-		set +f
+		set -f; set -- $line; set +f
 		case "$1" in
 			title)
 				entry_result
 				shift 1
-				title="$(echo "$@" | sed 's/://g')"
-				if echo "$title" | grep -q '(on /dev/[^)]*)$'; then
+				#title="$(echo "$@" | sed 's/://g')"
+				strchar_delete "$*" ':'; title="$value"
+				#if echo "$title" | grep -q '(on /dev/[^)]*)$'; then
+				_t="${title%(on /dev/[!)]*)}"
+				if [ ${#_t} -ne ${#title} ]; then  # was "(on /dev/...)"
 					log "Skipping entry '$title':"
 					log "appears to be an automatic reference taken from another menu.lst"
 					ignore_item=1
@@ -54,7 +52,8 @@ parse_grub_menu () {
 				# Hack alert: sed off any (hdn,n) but
 				# assume the kernel is on the same
 				# partition.
-				kernel="$(echo "$2" | sed 's/(.*)//')"
+				#kernel="$(echo "$2" | sed 's/(.*)//')"
+				strmid_delete "$2" '(' ')'; kernel="$value"
 				shift 2
 				parameters="$@"
 				# Systems with a separate /boot will not have
@@ -66,7 +65,8 @@ parse_grub_menu () {
 			initrd)
 				# Hack alert take 2: sed off any (hdn,n)
 				# See #566102
-				initrd="$(echo "$2" | sed 's/(.*)//')"
+				#initrd="$(echo "$2" | sed 's/(.*)//')"
+				strmid_delete "$2" '(' ')'; initrd="$value"
 				# Initrd same.
 				if [ "$partition" != "$bootpart" ]; then
 					initrd="/boot$initrd"
@@ -86,6 +86,14 @@ parse_grub_menu () {
 	entry_result
 }
 
+trap parse_done EXIT HUP INT QUIT TERM
+
+while read partition bootpart mpoint type um_root um_boot; do
+# echo $0: $read $partition $bootpart $mpoint $type $um_root $um_boot 1>&2
+
+found_item=0
+lineno=0
+
 grubconf=
 if [ -e "$mpoint/boot/grub/menu.lst" ]; then
 	grubconf="menu.lst"
@@ -93,15 +101,19 @@ elif [ -e "$mpoint/boot/grub/grub.conf" ]; then
 	grubconf="grub.conf"
 fi
 
-if [ "$grubconf" ] && \
-   ([ ! -e "$mpoint/boot/grub/grub.cfg" ] || \
-    [ "$mpoint/boot/grub/$grubconf" -nt "$mpoint/boot/grub/grub.cfg" ]); then
-	debug "parsing $grubconf"
+# Parse now using grub1 format unless grub.cfg exists and is newer.
+if [ "$grubconf" -a               ! -e "$mpoint/boot/grub/grub.cfg" \
+  -o "$mpoint/boot/grub/$grubconf" -nt "$mpoint/boot/grub/grub.cfg" ]; then
+	# debug "parsing $grubconf"
+	debug "${0##*/}: $parse_part: parsing $mpoint/boot/grub/$grubconf"
 	parse_grub_menu "$mpoint" "$partition" "$bootpart" < "$mpoint/boot/grub/$grubconf"
+	parse_done
 fi
 
 if [ "$found_item" = 0 ]; then
-	exit 1
+	echo "$partition $bootpart $mpoint $type $um_root $um_boot"  # failure
 else
-	exit 0
+	linux_unmount_boot "$mpoint" "$um_root" "$um_boot"
 fi
+
+done
diff --git a/linux-boot-probes/mounted/x86/50lilo b/linux-boot-probes/mounted/x86/50lilo
index a011b56..6c30bde 100755
--- a/linux-boot-probes/mounted/x86/50lilo
+++ b/linux-boot-probes/mounted/x86/50lilo
@@ -1,27 +1,7 @@
 #!/bin/sh
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 set -e
 
-partition="$1"
-bootpart="$2"
-mpoint="$3"
-type="$4"
-
-found_item=0
-
-title=""
-rootdev=""
-kernel=""
-parameters=""
-initrd=""
-read_only=""
-added_parameters=0
-default_rootdev=""
-default_kernel=""
-default_parameters=""
-default_initrd=""
-default_read_only=""
-
 dequote () {
 	item="${1%\"}"
 	echo "${item#\"}"
@@ -48,7 +28,7 @@ recordstanza () {
 				parameters="root=$rootdev $parameters"
 			fi
 			parameters="${parameters% }"
-			result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"
+			result "$rootpart:$bootpart:$title:$kernel:$initrd:$parameters"  # success
 			found_item=1
 		else
 			debug "cannot find $kernel or $initrd, not recording"
@@ -76,10 +56,8 @@ parse_lilo_conf () {
 	bootpart="$3"
 	IFS=" 	="
 	while read line; do
-		debug "parsing: $line"
-		set -f
-		set -- $line
-		set +f
+		# debug "parsing: $line"
+		set -f; set -- $line; set +f
 		case "$1" in
 			root)
 				rootdev=$(dequote "$2")
@@ -114,13 +92,33 @@ parse_lilo_conf () {
 	recordstanza
 }
 
+while read partition bootpart mpoint type um_root um_boot; do
+# echo $0: $read $partition $bootpart $mpoint $type $um_root $um_boot 1>&2
+
+found_item=0
+
+title=""
+rootdev=""
+kernel=""
+parameters=""
+initrd=""
+read_only=""
+added_parameters=0
+default_rootdev=""
+default_kernel=""
+default_parameters=""
+default_initrd=""
+default_read_only=""
+
 if [ -e "$mpoint/etc/lilo.conf" ]; then
 	debug "parsing lilo.conf"
 	parse_lilo_conf "$mpoint" "$partition" "$bootpart" < "$mpoint/etc/lilo.conf"
 fi
 
 if [ "$found_item" = 0 ]; then
-	exit 1
+	echo "$partition $bootpart $mpoint $type $um_root $um_boot"  # failure
 else
-	exit 0
+	linux_unmount_boot "$mpoint" "$um_root" "$um_boot"
 fi
+
+done
diff --git a/os-prober b/os-prober
index 54798ae..f821d5f 100755
--- a/os-prober
+++ b/os-prober
@@ -1,7 +1,22 @@
 #!/bin/sh
 set -e
 
-. /usr/share/os-prober/common.sh
+# $1: optional alternate root for scripts in this package
+# (for testing without install, etc.)
+# Use the default directories if $1 is not supplied.
+export    execpfx="${1:-/usr/share/os-prober}"
+export libexecpfx="${1:-/usr/libexec}/os-probes"
+
+# dash shell does not have "{varname}>&1" feature that bash shell has
+# for auto-assignment of new filedescriptors.
+# It is cumbersome to write the 'eval' to use our own variables.
+# Therefore use fixed numbers.
+export fd_result=3  # file descriptor for external results
+export fd_logger=9  # file descriptor for input to logger
+
+# export PS4=' ${FUNCNAME[0]}@$LINENO < ${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]} :'
+
+. $execpfx/common.sh  # note: binds fd_result and fd_logger
 
 newns "$@"
 require_tmpdir
@@ -14,15 +29,19 @@ log_output () {
 	fi
 }
 
-on_sataraid () {
-	type dmraid >/dev/null 2>&1 || return 1
-	local parent="${1%/*}"
-	local device="/dev/${parent##*/}"
-	if dmraid -r -c | grep -q "$device"; then
-		return 0
-	fi
-	return 1
-}
+    on_sataraid() { return 1; }  # default always returns false
+if type dmraid >/dev/null 2>&1; then
+    dmraid_r_c="$(dmraid -r -c)" || :  # a constant!
+    on_sataraid () {  # re-define
+        local parent="${1%/*}"
+        local device="/dev/${parent##*/}"
+        local t="${dmraid_r_c#*$device}"  # Is $device in $dmraid_r_c ?
+        if [ ${#t} -ne ${#dmraid_r_c} ]; then  # yes
+            return 0  # true
+        fi
+        return 1  # false
+    }
+fi
 
 partitions () {
 	# Exclude partitions that have whole_disk sysfs attribute set.
@@ -30,9 +49,13 @@ partitions () {
 		# Exclude partitions on physical disks that are part of a
 		# Serial ATA RAID disk.
 		for part in /sys/block/*/*[0-9]; do
-			if [ -f "$part/start" ] && \
-			   [ ! -f "$part/whole_disk" ] && ! on_sataraid $part; then
-				name="$(echo "${part##*/}" | sed 's,[!.],/,g')"
+			if [ -f "$part/start" -a ! -f "$part/whole_disk" ]  \
+			&& ! on_sataraid $part; then
+				name="${part##*/}"
+				# An empty name designates a root.
+				if [ 0 -eq ${#name} ]; then
+					name='/'
+				fi
 				if [ -e "/dev/$name" ]; then
 					echo "/dev/$name"
 				fi
@@ -50,11 +73,11 @@ partitions () {
 		fi
 	elif [ "$(uname -s)" = Linux ]; then
 		echo "Cannot find list of partitions!  (Try mounting /sys.)" >&2
-		exit 1
+		return 1
 	else
 		# We don't know how to probe OSes on non-Linux kernels.  For
 		# now, just don't get in the way.
-		exit 0
+		return 0
 	fi
 
 	# Add MD RAID devices
@@ -64,41 +87,42 @@ partitions () {
 
 	# Also detect OSes on LVM volumes (assumes LVM is active)
 	if type lvs >/dev/null 2>&1; then
-		echo "$(LVM_SUPPRESS_FD_WARNINGS=1 log_output lvs --noheadings --separator : -o vg_name,lv_name |
-			sed "s|-|--|g;s|^[[:space:]]*\(.*\):\(.*\)$|/dev/mapper/\1-\2|")"
+		LVM_SUPPRESS_FD_WARNINGS=1 log_output \
+		   lvs --noheadings --separator : -o vg_name,lv_name  2>/dev/null  |
+		sed "s|-|--|g;s|^[[:space:]]*\(.*\):\(.*\)$|/dev/mapper/\1-\2|"
+		# Double the original dashes to distinguish them from the
+		# single dash that will be added later.  Ignore leading
+		# whitespace, then find two fields separated by a colon
+		# and replace using /dev/mapper with the fields separated
+		# by a dash.
 	fi
 }
 
-parse_proc_swaps () {
-	while read line; do
-		set -f
-		set -- $line
-		set +f
-		echo "$(mapdevfs $1) swap"
-	done
-}
-
-parse_proc_mdstat () {
+parse_proc_mdstat () {  # filters stdin
+		udevinfo () { return 1; }  # default is false and empty
 	if type udevadm >/dev/null 2>&1; then
-		udevinfo () {
-			udevadm info "$@"
-		}
+		udevinfo () { udevadm info "$@" 2>/dev/null; }  # re-define
 	fi
+	local _udi _t
 	while read line; do
 		for word in $line; do
-			dev="${word%%[*}"
+			dev="${word%%[*}"  # ]
 			# TODO: factor this out to something in di-utils if
 			# it's needed elsewhere
-			if [ -d /sys/block ] && type udevinfo >/dev/null 2>&1; then
-				if ! udevinfo -q path -n "/dev/$dev" 2>/dev/null | \
-				     grep -q '/.*/.*/'; then
+			if [ -d /sys/block ]; then
+				_udi="$(udevinfo -q path -n "/dev/$dev")"
+				_t="${_udi%/*/*/*}"  # trim any 3 '/' and suffix
+				if [ ${#_t} -eq ${#_udi} ]; then  # no match
+					continue
+				fi
+			else
+				_t="${dev%/part*}"  # trim "/part" and suffix
+				if [ ${#_t} -eq ${#dev} ]; then  # no match
 					continue
 				fi
-			elif ! echo "$dev" | grep -q "/part"; then
-				continue
 			fi
 			raidpart="/dev/$dev"
-			echo "$(mapdevfs "$raidpart")"
+			echo ":$(mapdevfs "$raidpart")"  # ':' marks beginning
 		done
 	done
 }
@@ -106,66 +130,110 @@ parse_proc_mdstat () {
 # Needed for idempotency
 rm -f /var/lib/os-prober/labels
 
-for prog in /usr/libexec/os-probes/init/*; do
-	if [ -x "$prog" ] && [ -f "$prog" ]; then
+for prog in $libexecpfx/init/*; do
+	if [ -r "$prog" -a -x "$prog" -a ! -d "$prog" ]; then
 		"$prog" || true
 	fi
 done
 
 # We need to properly canonicalize partitions with mount points and partitions
 # used in RAID
-grep "^/dev/" /proc/mounts | parse_proc_mounts >"$OS_PROBER_TMP/mounted-map" || true
-: >"$OS_PROBER_TMP/swaps-map"
-if [ -f /proc/swaps ]; then
-	grep "^/dev/" /proc/swaps | parse_proc_swaps >"$OS_PROBER_TMP/swaps-map" || true
-fi
-: >"$OS_PROBER_TMP/raided-map"
-if [ -f /proc/mdstat ] ; then
-	grep "^md" /proc/mdstat | cut -d: -f2- | parse_proc_mdstat >"$OS_PROBER_TMP/raided-map" || true
-fi
 
-for partition in $(partitions); do
+# Parse /proc/mounts.
+# bash would use two associative arrays, indexed by $part, to store results.
+# dash has no arrays, so use variables with stylized names.
+while read part mpoint fs_type _rest; do
+    _t="${part#/dev/}"; if [ ${#_t} -ne ${#part} -a "$mpoint" -a "$fs_type" ]; then
+        str_tame "$(mapdevfs "$part")"
+        eval 'MPOINT_'$value'="$mpoint" FS_TYPE_'$value'="$fs_type"'
+    fi
+done  </proc/mounts
+
+# Parse /proc/swaps.
+# bash would use an associative array, indexed by $part, to store results.
+# dash has no arrays, so use variables with stylized names.
+while read part _rest; do
+    _t="${part#/dev/}"; if [ ${#_t} -ne ${#part} ]; then
+        # line from file begins with "/dev/"
+        str_tame "$(mapdevfs "$part")"
+        eval SWAP_$value=1
+    fi
+done  </proc/swaps
+
+# Store results in a string, with $part marked by a prefix ':'.
+raided=""
+[[ -f /proc/mdstat ]] && raided=$(grep "^md" /proc/mdstat | cut -d: -f2- | parse_proc_mdstat )
+
+pipe_mounted=""
+for test in $libexecpfx/mounted/* \
+            $libexecpfx/mounted/x86/* \
+            $libexecpfx/mounted/common/*  # last two lines: test without install
+do
+    if [ -r "$test" -a -x "$test" -a ! -d "$test" ]; then
+        pipe_mounted="$pipe_mounted|$test"
+    fi
+done
+pipe_mounted="${pipe_mounted#|}"
+# echo pipe_mounted= "$pipe_mounted" 1>&2
+
+pipe_not_yet=""
+for test in $libexecpfx/*; do
+    if [ -r "$test" -a -x "$test" -a ! -d "$test" ]; then
+        pipe_not_yet="$pipe_not_yet|$test"
+    fi
+done
+pipe_not_yet="${pipe_not_yet#|}"
+# echo pipe_not_yet= "$pipe_not_yet" 1>&2
+
+# Arrange the processing as a chain of filters, to enable parallelism.
+# When a filter recognizes a partition, then the filter uses the 'result'
+# function to write the details to the fd_result pipe.  Each filter
+# passes any unrecognized partition on to the next filter by using stdout.
+# Logging is performed by writing to fd_logger, which goes to a single
+# logger process that reads its stdin.  Stderr remains available for
+# debugging and emergencies.
+# Plumbing by John Reiser (jreiser bitwagon com), November 2012.
+
+partitions | ( ( (
+    while read partition ; do
+        # Distribute to proper entrypoint in pipeline of filters.
+
 	if ! mapped="$(mapdevfs "$partition")"; then
 		log "Device '$partition' does not exist; skipping"
 		continue
 	fi
-
-	# Skip partitions used in software RAID arrays
-	if grep -q "^$mapped" "$OS_PROBER_TMP/raided-map" ; then
+        str_tame "$mapped"; tamed="$value"
+	raid="${raided%:$mapped}"; if [ ${#raid} -ne ${#raided} ]; then
 		debug "$partition: part of software raid array"
 		continue
 	fi
-
-	# Skip partitions used as active swap
-	if grep -q "^$mapped " "$OS_PROBER_TMP/swaps-map" ; then
+	eval 'swap="$SWAP_'$tamed'"'; if [ -n "$swap" ]; then
 		debug "$partition: is active swap"
 		continue
 	fi
-
-	if ! grep -q "^$mapped " "$OS_PROBER_TMP/mounted-map" ; then
-		for test in /usr/libexec/os-probes/*; do
-			if [ -f "$test" ] && [ -x "$test" ]; then
-				debug "running $test on $partition"
-				if "$test" "$partition"; then
-					debug "os detected by $test"
-			   		break
-				fi
-			fi
-		done
-	else
-		mpoint=$(grep "^$mapped " "$OS_PROBER_TMP/mounted-map" | head -n1 | cut -d " " -f 2)
+	eval 'mpoint="$MPOINT_'$tamed'"'; if [ -z "$mpoint" ]; then
+		echo "$partition"  # not yet mounted
+	else  # already mounted
 		mpoint="$(unescape_mount "$mpoint")"
-		if [ "$mpoint" != "/target/boot" ] && [ "$mpoint" != "/target" ] && [ "$mpoint" != "/" ]; then
-			type=$(grep "^$mapped " "$OS_PROBER_TMP/mounted-map" | head -n1 | cut -d " " -f 3)
-			for test in /usr/libexec/os-probes/mounted/*; do
-				if [ -f "$test" ] && [ -x "$test" ]; then
-					debug "running $test on mounted $partition"
-					if "$test" "$partition" "$mpoint" "$type"; then
-						debug "os detected by $test"
-						break
-					fi
-				fi
-			done
+		if [ "$mpoint" != "/target/boot" \
+		  -a "$mpoint" != "/target"      \
+		  -a "$mpoint" != "/" ];       then
+			eval 'type="$FS_TYPE_'$tamed'"'
+			echo "$partition $mpoint $type 0"  1>&8  # 0: do not unmount
 		fi
 	fi
-done
+    done  \
+       | eval "${pipe_not_yet}"
+) 8>&1 | eval "${pipe_mounted}"  |  while read line; do
+	    echo '?'unknown "$line"
+	    set -f; set -- $line; set +f
+	    part="$1"; dir="$2"; type="$3"; discard="$4"
+	    if [ 1 == "$discard" ]; then
+		umount "$dir"
+		rmdir  "$dir"
+	    fi
+	done 1>&2
+) 9>&1 | logger 1>&-  # fd_logger
+) 3>&1  # fd_result
+
+# EOF os-prober
diff --git a/os-probes/common/50mounted-tests b/os-probes/common/50mounted-tests
index 4e0f6d6..33824d5 100755
--- a/os-probes/common/50mounted-tests
+++ b/os-probes/common/50mounted-tests
@@ -1,49 +1,63 @@
 #!/bin/sh
 # Sub-tests that require a mounted partition.
 set -e
-partition="$1"
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
+delay_types=""  # file system types that should be checked last
+kfs_types=""  # kernel file system types
+for mod_type in $(grep -v nodev /proc/filesystems); do
+	# hfsplus filesystems are mountable as hfs. Try hfs last so
+	# that we can tell the difference.
+	if [ "$mod_type" = hfs ]; then
+		delay_types="$delay_types $mod_type"
+	elif [ "$mod_type" = fuseblk ]; then
+		if type ntfs-3g >/dev/null 2>&1; then
+			kfs_types="$kfs_types ntfs-3g"
+		fi
+	else
+		kfs_types="$kfs_types $mod_type"
+	fi
+done
+
+tmpmntdir=/var/lib/os-prober/mount
+mkdir  "$tmpmntdir" >/dev/null 2>&1 || true
+(cd $tmpmntdir
+    umount -f osp* >/dev/null 2>&1 || true
+    rmdir     osp* >/dev/null 2>&1 || true
+)
+
+
+while read partition; do
+
+tmpmnt=$tmpmntdir/osp$(( mount_count+=1 ))
+mkdir $tmpmnt
+
+xtra_types=""
 types="$(fs_type "$partition")" || types=NOT-DETECTED
-if [ "$types" = NOT-DETECTED ]; then
+case "$types" in
+NOT-DETECTED)
 	debug "$1 type not recognised; skipping"
-	exit 0
-elif [ "$types" = swap ]; then
+	continue ;;
+swap)
 	debug "$1 is a swap partition; skipping"
-	exit 0
-elif [ "$types" = crypto_LUKS ]; then
+	continue ;;
+crypto_LUKS)
 	debug "$1 is a LUKS partition; skipping"
-	exit 0
-elif [ "$types" = ntfs ]; then
+	continue ;;
+ntfs)
 	if type ntfs-3g >/dev/null 2>&1; then
-		types='ntfs-3g ntfs'
-	fi
-elif [ -z "$types" ]; then
+		xtra_types='ntfs-3g ntfs'  
+	fi ;;
+?*)  # non-null
+	xtra_types="$types" ;;  # guess this one first
+'')  # null
 	if type cryptsetup >/dev/null 2>&1 && \
 	   cryptsetup luksDump "$partition" >/dev/null 2>&1; then
 		debug "$1 is a LUKS partition; skipping"
-		exit 0
-	fi
-	for type in $(grep -v nodev /proc/filesystems); do
-		# hfsplus filesystems are mountable as hfs. Try hfs last so
-		# that we can tell the difference.
-		if [ "$type" = hfs ]; then
-			delaytypes="${delaytypes:+$delaytypes }$type"
-		elif [ "$type" = fuseblk ]; then
-			if type ntfs-3g >/dev/null 2>&1; then
-				types="${types:+$types }ntfs-3g"
-			fi
-		else
-			types="${types:+$types }$type"
-		fi
-	done
-fi
-
-tmpmnt=/var/lib/os-prober/mount
-if [ ! -d "$tmpmnt" ]; then
-	mkdir "$tmpmnt"
-fi
+		continue
+	fi ;;
+esac
 
 mounted=
 if type grub-mount >/dev/null 2>&1 && \
@@ -59,9 +73,9 @@ if type grub-mount >/dev/null 2>&1 && \
 	fi
 else
 	ro_partition "$partition"
-	for type in $types $delaytypes; do
+	for type in $xtra_types $kfs_types $delay_types; do
 		if mount -o ro -t "$type" "$partition" "$tmpmnt" 2>/dev/null; then
-			debug "mounted as $type filesystem"
+			# debug "mounted as $type filesystem"
 			mounted=1
 			break
 		fi
@@ -69,25 +83,9 @@ else
 fi
 
 if [ "$mounted" ]; then
-	for test in /usr/libexec/os-probes/mounted/*; do
-		debug "running subtest $test"
-		if [ -f "$test" ] && [ -x "$test" ]; then
-			if "$test" "$partition" "$tmpmnt" "$type"; then
-				debug "os found by subtest $test"
-				if ! umount "$tmpmnt"; then
-					warn "failed to umount $tmpmnt"
-				fi
-				rmdir "$tmpmnt" || true
-				exit 0
-			fi
-		fi
-	done
-	if ! umount "$tmpmnt"; then
-		warn "failed to umount $tmpmnt"
-	fi
+	echo "$partition $tmpmnt $type 1"  # 1: must unmount
+else
+	rmdir "$tmpmnt" || :
 fi
 
-rmdir "$tmpmnt" || true
-
-# No tests found anything.
-exit 1
+done
diff --git a/os-probes/mounted/common/40lsb b/os-probes/mounted/common/40lsb
index ce8d4e1..2b7e576 100755
--- a/os-probes/mounted/common/40lsb
+++ b/os-probes/mounted/common/40lsb
@@ -2,30 +2,25 @@
 # Test for LSB systems.
 set -e
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
-partition="$1"
-dir="$2"
-type="$3"
-
-lsb_field () {
-	file="$1"
-	field="$2"
-	grep ^"$field" "$file" | cut -d = -f 2 | sed 's/^"//' | sed 's/"$//' | sed 's/:/ /g'
-}
+while read partition dir type discard; do
 
 file="$dir/etc/lsb-release"
 if [ ! -e "$file" ]; then
-	exit 1
+    echo "$partition $dir $type $discard"; continue  # failure
 fi
 
-release=$(lsb_field "$file" DISTRIB_RELEASE)
+while read line; do eval "$line"; done < $file
+
+release="$DISTRIB_RELEASE"
 if [ -z "$release" ]; then
-	release=$(lsb_field "$file" DISTRIB_CODENAME)
+	release="$DISTRIB_CODENAME"
 fi
-description=$(lsb_field "$file" DISTRIB_DESCRIPTION)
+
+description="$DISTRIB_DESCRIPTION"
 if [ -z "$description" ]; then
-	description=$(lsb_field "$file" DISTRIB_CODENAME)
+	description="$DISTRIB_CODENAME"
 fi
 
 if [ -n "$description" ]; then
@@ -35,14 +30,19 @@ if [ -n "$description" ]; then
 		long="$description"
 	fi
 else
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
 
-short=$(lsb_field "$file" DISTRIB_ID | sed 's/ //g')
+strchar_delete "$DISTRIB_ID" ' '; short="$value"
 if [ -z "$short" ]; then
 	short="UnknownLSB"
 fi
 
-label="$(count_next_label "$short")"
-result "$partition:$long:$label:linux"
-exit 0
+result "$partition:$long:$short$((label += 1)):linux"  # success
+
+if [ 1 == "$discard" ]; then
+    umount $dir
+    rmdir $dir
+fi
+
+done
diff --git a/os-probes/mounted/common/90linux-distro b/os-probes/mounted/common/90linux-distro
index fb56b61..87ec273 100755
--- a/os-probes/mounted/common/90linux-distro
+++ b/os-probes/mounted/common/90linux-distro
@@ -2,134 +2,196 @@
 # Test for linux distributions.
 set -e
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
+path_lookup cat; export cat  # search once only
 
-partition="$1"
-dir="$2"
-type="$3"
+origdir=$PWD
+
+function fail() {
+	echo "$partition $dir $type $discard"  1>&7  # failure
+
+	cd $origdir
+}
+
+function succeed() {
+	echo "$partition:$long:$short"  1>&6  # success
+
+	cd $origdir
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
+}
+
+function filter() {
+  while read partition dir type discard; do
 
 # This test is inaccurate, but given separate / and /boot partitions and the
 # fact that only some architectures have ld-linux.so, I can't see anything
 # better. Make sure this test has a high number so that more accurate tests
 # can come first.
 # TODO: look for ld-linux.so on arches that have it
-if ls "$dir"/lib*/ld*.so* >/dev/null 2>/dev/null && [ -d "$dir/boot" ] \
-	|| ls "$dir"/usr/libexec*/ld*.so* >/dev/null 2>/dev/null; then
-	if [ -e "$dir/etc/debian_version" ]; then
+
+# Check for any actual file that matches the pattern.
+# If pathname expansion finds no matches then it returns the pattern itself,
+#                                          and the '[ -r "$1" ]' fails.
+# If pathname expansion does find a match then the '[ -r "$1" ]' succeeds.
+# The syntax with braces avoids using a subprocess.
+# All that saves 0.02 seconds over using the exit code of /bin/ls.
+# It matters when you have several dozen partitions to check.
+    if { set -- "$dir"/lib*/ld*.so*;         [ -r "$1" ]; } \
+    || { set -- "$dir"/usr/libexec*/ld*.so*; [ -r "$1" ]; } ; then
+	cd "$dir"/etc 2>/dev/null || { fail; continue; }
+	short=""
+	set -- *[_-]version
+
+	for file do case "$file" in
+	debian_version)
 		short="Debian"
-		long="$(printf "Debian GNU/Linux (%s)\n" "$(cat "$dir/etc/debian_version")")"
+		long="$(printf "Debian GNU/Linux (%s)\n" "$($cat "$dir/etc/debian_version")")" ;;
+	kanotix-version)
+		short="Kanotix"
+		long="$($cat "$dir/etc/kanotix-version")" ;;
+	slackware-version)
+		short="Slackware"
+		long="$(printf "Slackware Linux (%s)\n" "$($cat "$dir/etc/slackware-version")")" ;;
+	esac; done
+	if [ -n "$short" ]; then succeed; continue; fi
+
 	# RPM derived distributions may also have a redhat-release or
-	# mandrake-release, so check their files first.
-	elif [ -e "$dir/etc/altlinux-release" ]; then
+	# mandrake-release.  Set base first, then override using derived.
+	set -- *-release
+
+	for file do case "$file" in  # base distro
+	redhat-release)
+		short="RedHat"
+		long="$($cat "$dir/etc/redhat-release")" ;;
+	esac; done
+
+	for file do case "$file" in  # possible intermediate distro
+	fedora-release)
+		short="Fedora"
+		long="$($cat "$dir/etc/fedora-release")" ;;
+	esac; done
+
+	for file do case "$file" in  # possible intermediate distro
+	mandrake-release)
+		short="Mandrake"
+		long="$($cat "$dir/etc/mandrake-release")" ;;
+	esac; done
+
+	for file do case "$file" in  # possible derived distro
+	SuSE-release)
+		short="SuSE"
+		long="$(head -n 1 "$dir/etc/SuSE-release")" ;;
+	altlinux-release)
 		short="ALTLinux"
-		long="$(cat "$dir/etc/altlinux-release")"
-	elif [ -e "$dir/etc/magic-release" ]; then
+		long="$($cat "$dir/etc/altlinux-release")" ;;
+	magic-release)
 		short="Magic"
-		long="$(cat "$dir/etc/magic-release")"
-	elif [ -e "$dir/etc/blackPanther-release" ]; then
+		long="$($cat "$dir/etc/magic-release")" ;;
+	blackPanther-release)
 		short="blackPanther"
-		long="$(cat "$dir/etc/blackPanther-release")"
-	elif [ -e "$dir/etc/ark-release" ]; then
+		long="$($cat "$dir/etc/blackPanther-release")" ;;
+	ark-release)
 		short="Ark"
-		long="$(cat "$dir/etc/ark-release")"
-	elif [ -e "$dir/etc/arch-release" ]; then
+		long="$($cat "$dir/etc/ark-release")" ;;
+	arch-release)
 		short="Arch"
-		long="$(cat "$dir/etc/arch-release")"
-	elif [ -e "$dir/etc/asplinux-release" ]; then
+		long="$($cat "$dir/etc/arch-release")" ;;
+	asplinux-release)
 		short="ASPLinux"
-		long="$(cat "$dir/etc/asplinux-release")"
-	elif [ -e "$dir/etc/lvr-release" ]; then
+		long="$($cat "$dir/etc/asplinux-release")" ;;
+	lvr-release)
 		short="LvR"
-		long="$(cat "$dir/etc/lvr-release")"
-	elif [ -e "$dir/etc/caos-release" ]; then
+		long="$($cat "$dir/etc/lvr-release")" ;;
+	caos-release)
 		short="cAos"
-		long="$(cat "$dir/etc/caos-release")"
-	elif [ -e "$dir/etc/aurox-release" ]; then
+		long="$($cat "$dir/etc/caos-release")" ;;
+	aurox-release)
 		short="Aurox"
-		long="$(cat "$dir/etc/aurox-release")"
-	elif [ -e "$dir/etc/engarde-release" ]; then
+		long="$($cat "$dir/etc/aurox-release")" ;;
+	engarde-release)
 		short="EnGarde"
-		long="$(cat "$dir/etc/engarde-release")"
-	elif [ -e "$dir/etc/vine-release" ]; then
+		long="$($cat "$dir/etc/engarde-release")" ;;
+	vine-release)
 		short="Vine"
-		long="$(cat "$dir/etc/vine-release")"
-	elif [ -e "$dir/etc/whitebox-release" ]; then
+		long="$($cat "$dir/etc/vine-release")" ;;
+	whitebox-release)
 		short="WhiteBox"
-		long="$(cat "$dir/etc/whitebox-release")"
-	elif [ -e "$dir/etc/pld-release" ]; then
+		long="$($cat "$dir/etc/whitebox-release")" ;;
+	pld-release)
 		short="PLD"
-		long="$(cat "$dir/etc/pld-release")"
-	elif [ -e "$dir/etc/startcom-release" ]; then
+		long="$($cat "$dir/etc/pld-release")" ;;
+	startcom-release)
 		short="StartCom"
-		long="$(cat "$dir/etc/startcom-release")"
-	elif [ -e "$dir/etc/trustix-release" ]; then
+		long="$($cat "$dir/etc/startcom-release")" ;;
+	trustix-release)
 		short="Trustix"
-		long="$(cat "$dir/etc/trustix-release")"
-	elif [ -e "$dir/etc/openna-release" ]; then
+		long="$($cat "$dir/etc/trustix-release")" ;;
+	openna-release)
 		short="OpenNA"
-		long="$(cat "$dir/etc/openna-release")"
-	elif [ -e "$dir/etc/conectiva-release" ]; then
+		long="$($cat "$dir/etc/openna-release")" ;;
+	conectiva-release)
 		short="Conectiva"
-		long="$(cat "$dir/etc/conectiva-release")"
-	elif [ -e "$dir/etc/mandrake-release" ]; then
-		short="Mandrake"
-		long="$(cat "$dir/etc/mandrake-release")"
-	elif [ -e "$dir/etc/fedora-release" ]; then
-		short="Fedora"
-		long="$(cat "$dir/etc/fedora-release")"
-	elif [ -e "$dir/etc/redhat-release" ]; then
-		short="RedHat"
-		long="$(cat "$dir/etc/redhat-release")"
-	elif [ -e "$dir/etc/SuSE-release" ]; then
-		short="SuSE"
-		long="$(head -n 1 "$dir/etc/SuSE-release")"
-	elif [ -e "$dir/etc/gentoo-release" ]; then
+		long="$($cat "$dir/etc/conectiva-release")" ;;
+	gentoo-release)
 		short="Gentoo"
-		long="$(cat "$dir/etc/gentoo-release")"
-	elif [ -e "$dir/etc/cobalt-release" ]; then
+		long="$($cat "$dir/etc/gentoo-release")" ;;
+	cobalt-release)
 		short="Cobalt"
-		long="$(cat "$dir/etc/cobalt-release")"
-	elif [ -e "$dir/etc/yellowdog-release" ]; then
+		long="$($cat "$dir/etc/cobalt-release")" ;;
+	yellowdog-release)
 		short="YellowDog"
-		long="$(cat "$dir/etc/yellowdog-release")"
-	elif [ -e "$dir/etc/turbolinux-release" ]; then
+		long="$($cat "$dir/etc/yellowdog-release")" ;;
+	turbolinux-release)
 		short="Turbolinux"
-		long="$(cat "$dir/etc/turbolinux-release")"
-	elif [ -e "$dir/etc/pardus-release" ]; then
+		long="$($cat "$dir/etc/turbolinux-release")" ;;
+	pardus-release)
 		short="Pardus"
-		long="$(cat "$dir/etc/pardus-release")"
-	elif [ -e "$dir/etc/kanotix-version" ]; then
-		short="Kanotix"
-		long="$(cat "$dir/etc/kanotix-version")"
-	elif [ -e "$dir/etc/slackware-version" ]; then
-		short="Slackware"
-		long="$(printf "Slackware Linux (%s)\n" "$(cat "$dir/etc/slackware-version")")"
-	elif [ -e "$dir/sbin/pkgtool" ]; then
+		long="$($cat "$dir/etc/pardus-release")" ;;
+	frugalware-release)
+		short="Frugalware Linux"
+		long="$($cat "$dir/etc/frugalware-release")" ;;
+	kdemar-release)
+		short="K-DEMar"
+		long="$(printf "K-DEMar GNU/Linux (%s)\n" "$($cat "$dir/etc/kdemar-release")")" ;;
+	lfs-release)
+		short="LFS"
+		long="$(printf "Linux From Scratch (%s)\n" "$($cat "$dir/etc/lfs-release")")" ;;
+	meego-release)
+		short="MeeGo"
+		long="$(head -1 "$dir/etc/meego-release")" ;;
+	esac; done
+	if [ -n "$short" ]; then succeed; continue; fi
+
+	# The oddballs.
+	if [ -e "$dir/sbin/pkgtool" ]; then
 		short="Slackware"
 		long="Slackware Linux"
 	elif grep -qs OpenLinux "$dir/etc/issue"; then
 		short="Caldera"
 		long="Caldera OpenLinux"
-	elif [ -e "$dir/etc/frugalware-release" ]; then
-		short="Frugalware Linux"
-		long="$(cat "$dir/etc/frugalware-release")"
-	elif [ -e "$dir/etc/kdemar-release" ]; then
-		short="K-DEMar"
-		long="$(printf "K-DEMar GNU/Linux (%s)\n" "$(cat "$dir/etc/kdemar-release")")"
-	elif [ -e "$dir/etc/lfs-release" ]; then
-		short="LFS"
-		long="$(printf "Linux From Scratch (%s)\n" "$(cat "$dir/etc/lfs-release")")"
-	elif [ -e "$dir/etc/meego-release" ]; then
-		short="MeeGo"
-		long="$(head -1 "$dir/etc/meego-release")"
 	else
 		short="Linux"
 		long="unknown Linux distribution"
 	fi
-	
-        label="$(count_next_label "$short")"
-	result "$partition:$long:$label:linux"
-	exit 0
-else
-	exit 1
-fi
+
+	succeed
+    else
+	fail
+    fi
+  done  # read next partition
+}
+
+# Use two parallel streams to filter alternating partitions.
+# Count the over-all results to avoid a race when incrementing the label.
+( ( ( while  read line; do   echo "$line" 1>&5  # side2
+          if read line; then echo "$line"       # side1
+          fi
+      done | filter 1>&-  # side1
+) 5>&1     | filter 1>&-  # side2
+) 6>&1     | while read line; do result "$line$((label += 1)):linux"; done
+) 7>&1
+
+# EOF
diff --git a/os-probes/mounted/powerpc/20macosx b/os-probes/mounted/powerpc/20macosx
index 16d9da6..e97f30d 100755
--- a/os-probes/mounted/powerpc/20macosx
+++ b/os-probes/mounted/powerpc/20macosx
@@ -6,10 +6,6 @@ partition="$1"
 mpoint="$2"
 type="$3"
 
-debug() {
-  logger -t macosx-prober "debug: $@"
-}
-
 # Weed out stuff that doesn't apply to us
 case "$type" in
   hfsplus) debug "$1 is an HFS+ partition" ;;
diff --git a/os-probes/mounted/powerpc/20macosx.macosxdummyfix b/os-probes/mounted/powerpc/20macosx.macosxdummyfix
index dd4207f..5ad29da 100755
--- a/os-probes/mounted/powerpc/20macosx.macosxdummyfix
+++ b/os-probes/mounted/powerpc/20macosx.macosxdummyfix
@@ -6,10 +6,6 @@ partition="$1"
 mpoint="$2"
 type="$3"
 
-debug() {
-  logger -t macosx-prober "debug: $@"
-}
-
 # Weed out stuff that doesn't apply to us
 case "$type" in
   hfsplus) debug "$1 is an HFS+ partition" ;;
diff --git a/os-probes/mounted/x86/10freedos b/os-probes/mounted/x86/10freedos
index 94388f3..a15a957 100755
--- a/os-probes/mounted/x86/10freedos
+++ b/os-probes/mounted/x86/10freedos
@@ -1,23 +1,26 @@
 #!/bin/sh
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
-partition="$1"
-mpoint="$2"
-type="$3"
+while read partition dir type discard; do
 
 # Weed out stuff that doesn't apply to us
 case "$type" in
 	vfat) debug "$1 is a FAT32 partition" ;;
 	msdos) debug "$1 is a FAT16 partition" ;;
 	fat) debug "$1 is a FAT partition (mounted by GRUB)" ;;
-	*) debug "$1 is not a FAT partition: exiting"; exit 1 ;;
+	*) # debug "$1 is not a FAT partition"
+	   echo "$partition $dir $type $discard"; continue ;;  # failure
 esac
 
 if item_in_dir -q kernel.sys "$2" && item_in_dir -q command.com "$2"; then
-	label="$(count_next_label FreeDOS)"
-	result "$1:FreeDOS:$label:chain"
-	exit 0
+	result "$1:FreeDOS:FreeDos$((label += 1)):chain"  # success
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
 else
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
+
+done
diff --git a/os-probes/mounted/x86/10qnx b/os-probes/mounted/x86/10qnx
index 8d40398..6f1dc70 100755
--- a/os-probes/mounted/x86/10qnx
+++ b/os-probes/mounted/x86/10qnx
@@ -1,21 +1,25 @@
 #!/bin/sh
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
-partition="$1"
-mpoint="$2"
-type="$3"
+while read partition dir type discard; do
 
 # Weed out stuff that doesn't apply to us
 case "$type" in
 	qnx4) debug "$partition is a QNX4 partition" ;;
-	*) debug "$partition is not a QNX4 partition: exiting"; exit 1 ;;
+	*) # debug "$partition is not a QNX4 partition"
+	   echo "$partition $dir $type $discard"; continue ;;  # failure
 esac
 
 if [ -e "$mpoint/.boot" ]; then
-	label="$(count_next_label QNX)"
-	result "$partition:QNX:$label:chain"
-	exit 0
+	result "$partition:QNX:QNX$((label += 1)):chain"  # success
+
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
 else
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
+
+done
diff --git a/os-probes/mounted/x86/20microsoft b/os-probes/mounted/x86/20microsoft
index 644d63c..f68d453 100755
--- a/os-probes/mounted/x86/20microsoft
+++ b/os-probes/mounted/x86/20microsoft
@@ -1,43 +1,44 @@
 #!/bin/sh
 # Detects all Microsoft OSes on a collection of partitions.
 
-. /usr/share/os-prober/common.sh
+set -e
 
-partition="$1"
-mpoint="$2"
-type="$3"
+. $execpfx/common.sh
+
+while read partition dir type discard; do
 
 # Weed out stuff that doesn't apply to us
 case "$type" in
-	ntfs|ntfs-3g) debug "$1 is a NTFS partition" ;;
-	vfat) debug "$1 is a FAT32 partition" ;;
-	msdos) debug "$1 is a FAT16 partition" ;;
-	fat) debug "$1 is a FAT partition (mounted by GRUB)" ;;
-	fuse|fuseblk) debug "$1 is a FUSE partition" ;; # might be ntfs-3g
-	*) debug "$1 is not a MS partition: exiting"; exit 1 ;;
+	ntfs|ntfs-3g) debug "$partition is a NTFS partition" ;;
+	vfat) debug "$partition is a FAT32 partition" ;;
+	msdos) debug "$partition is a FAT16 partition" ;;
+	fat) debug "$partition is a FAT partition (mounted by GRUB)" ;;
+	fuse|fuseblk) debug "$partition is a FUSE partition" ;; # might be ntfs-3g
+	*) # debug "$partition is not a MS partition"
+	   echo "$partition $dir $type $discard"; continue ;;  # failure
 esac
 
 found=
 # Vista (previously Longhorn)
-if item_in_dir -q bootmgr "$2"; then
+if item_in_dir -q bootmgr "$dir"; then
 	# there might be different boot directories in different case as:
 	# boot Boot BOOT
-	for boot in $(item_in_dir boot "$2"); do
-		bcd=$(item_in_dir bcd "$2/$boot")
+	for boot in $(item_in_dir boot "$dir"); do
+		bcd=$(item_in_dir bcd "$dir/$boot")
 		if [ -n "$bcd" ]; then
-			if grep -qs "W.i.n.d.o.w.s. .8" "$2/$boot/$bcd"; then
+			if grep -qs "W.i.n.d.o.w.s. .8" "$dir/$boot/$bcd"; then
 				long="Windows 8 (loader)"
-			elif grep -qs "W.i.n.d.o.w.s. .7" "$2/$boot/$bcd"; then
+			elif grep -qs "W.i.n.d.o.w.s. .7" "$dir/$boot/$bcd"; then
 				long="Windows 7 (loader)"
-			elif grep -qs "W.i.n.d.o.w.s. .V.i.s.t.a" "$2/$boot/$bcd"; then
+			elif grep -qs "W.i.n.d.o.w.s. .V.i.s.t.a" "$dir/$boot/$bcd"; then
 				long="Windows Vista (loader)"
-			elif grep -qs "W.i.n.d.o.w.s. .S.e.r.v.e.r. .2.0.0.8. .R.2." "$2/$boot/$bcd"; then
+			elif grep -qs "W.i.n.d.o.w.s. .S.e.r.v.e.r. .2.0.0.8. .R.2." "$dir/$boot/$bcd"; then
 				long="Windows Server 2008 R2 (loader)"
-			elif grep -qs "W.i.n.d.o.w.s. .S.e.r.v.e.r. .2.0.0.8." "$2/$boot/$bcd"; then
+			elif grep -qs "W.i.n.d.o.w.s. .S.e.r.v.e.r. .2.0.0.8." "$dir/$boot/$bcd"; then
 				long="Windows Server 2008 (loader)"
-			elif grep -qs "W.i.n.d.o.w.s. .R.e.c.o.v.e.r.y. .E.n.v.i.r.o.n.m.e.n.t" "$2/$boot/$bcd"; then
+			elif grep -qs "W.i.n.d.o.w.s. .R.e.c.o.v.e.r.y. .E.n.v.i.r.o.n.m.e.n.t" "$dir/$boot/$bcd"; then
 				long="Windows Recovery Environment (loader)"
-			elif grep -qs "W.i.n.d.o.w.s. .S.e.t.u.p" "$2/$boot/$bcd"; then
+			elif grep -qs "W.i.n.d.o.w.s. .S.e.t.u.p" "$dir/$boot/$bcd"; then
 				long="Windows Recovery Environment (loader)"
 			else
 				long="Windows Vista (loader)"
@@ -52,25 +53,25 @@ if item_in_dir -q bootmgr "$2"; then
 fi
 
 # 2000/XP/NT4.0
-if [ -z "$found" ] && item_in_dir -q ntldr "$2" && item_in_dir -q ntdetect.com "$2"; then
+if [ -z "$found" ] && item_in_dir -q ntldr "$dir" && item_in_dir -q ntdetect.com "$dir"; then
 	long="Windows NT/2000/XP"
 	short=Windows
-	ini=$(item_in_dir boot.ini "$2")
+	ini=$(item_in_dir boot.ini "$dir")
 	if [ -n "$ini" ]; then
-		multicount="$(grep -e "^multi" "$2/$ini" | wc -l)"
-		scsicount="$(grep -e "^scsi" "$2/$ini" | wc -l)"
+		multicount="$(grep -e "^multi" "$dir/$ini" | wc -l)"
+		scsicount="$(grep -e "^scsi" "$dir/$ini" | wc -l)"
 		msoscount="$(expr "${multicount}" + "${scsicount}")"
 		if [ "$msoscount" -eq 1 ]; then
 			# We need to remove a Carriage Return at the end of
 			# the line...
-			defaultmspart="$(grep -e "^default=" "$2/$ini" | cut -d '=' -f2 | tr -d '\r')"
+			defaultmspart="$(grep -e "^default=" "$dir/$ini" | cut -d '=' -f2 | tr -d '\r')"
 			# Escape any backslashes in defaultmspart
 			grepexp="^$(echo "$defaultmspart" | sed -e 's/\\/\\\\/')="
 			# Colons not allowed; replace by spaces
 			# Accented characters (non UTF-8) cause debconf to
 			# hang, so we fall back to the default if the name
 			# contains any weird characters.
-			long="$(grep -e "$grepexp" "$2/$ini" | cut -d '"' -f2 | \
+			long="$(grep -e "$grepexp" "$dir/$ini" | cut -d '"' -f2 | \
 				tr ':' ' ' | LC_ALL=C grep -v '[^a-zA-Z0-9 &()/_-]')"
 			if [ -z "$long" ]; then
 				long="Windows NT/2000/XP"
@@ -84,7 +85,7 @@ if [ -z "$found" ] && item_in_dir -q ntldr "$2" && item_in_dir -q ntdetect.com "
 fi
 
 # MS-DOS
-if [ -z "$found" ] && item_in_dir -q dos "$2"; then
+if [ -z "$found" ] && item_in_dir -q dos "$dir"; then
 	long="MS-DOS 5.x/6.x/Win3.1"
 	short=MS-DOS
 
@@ -92,8 +93,8 @@ if [ -z "$found" ] && item_in_dir -q dos "$2"; then
 fi
 
 # 95/98/Me
-if [ -z "$found" ] && item_in_dir -q windows "$2" &&
-     item_in_dir -q win.com "$2"/"$(item_in_dir windows "$2")"; then
+if [ -z "$found" ] && item_in_dir -q windows "$dir" &&
+     item_in_dir -q win.com "$dir"/"$(item_in_dir windows "$dir")"; then
 	long="Windows 95/98/Me"
 	short=Windows9xMe
 
@@ -101,9 +102,13 @@ if [ -z "$found" ] && item_in_dir -q windows "$2" &&
 fi
 
 if [ -z "$found" ]; then
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
 
-label="$(count_next_label "$short")"
-result "${partition}:${long}:${label}:chain"
-exit 0
+result "${partition}:${long}:${short}$((label += 1)):chain"  # success
+
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
+done
diff --git a/os-probes/mounted/x86/30utility b/os-probes/mounted/x86/30utility
index af48d30..995e523 100755
--- a/os-probes/mounted/x86/30utility
+++ b/os-probes/mounted/x86/30utility
@@ -1,18 +1,19 @@
 #!/bin/sh
 # Detects utility (hw vendor recovery) partitions.
 
-. /usr/share/os-prober/common.sh
+set -e
 
-partition="$1"
-mpoint="$2"
-type="$3"
+. $execpfx/common.sh
+
+while read partition dir type discard; do
 
 # Weed out stuff that doesn't apply to us
 case "$type" in
 	vfat)	debug "$1 is a FAT32 partition" ;;
 	msdos)	debug "$1 is a FAT16 partition" ;;
 	fat)	debug "$1 is a FAT partition (mounted by GRUB)" ;;
-	*)	debug "$1 is not a FAT partition: exiting"; exit 1 ;;
+	*)    # debug "$1 is not a FAT partition"
+		echo "$partition $dir $type $discard"; continue ;;  # failure
 esac
 
 # Dell Utility partitions have partition type 0xde, but no idea how to
@@ -25,9 +26,14 @@ elif item_in_dir -q f11.sys "$2"; then
 	long="Acronis Secure Zone"
 	short=AcroneZone
 else
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
 
-label="$(count_next_label "$short")"
-result "${partition}:${long}:${label}:chain"
-exit 0
+result "${partition}:${long}:${short}$((label += 1)):chain"  # success
+
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
+
+done
diff --git a/os-probes/mounted/x86/70hurd b/os-probes/mounted/x86/70hurd
index ffc0a44..3e23ee0 100755
--- a/os-probes/mounted/x86/70hurd
+++ b/os-probes/mounted/x86/70hurd
@@ -1,16 +1,19 @@
 #!/bin/sh
 set -e
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
-partition="$1"
-dir="$2"
-type="$3"
+while read partition dir type discard; do
 
 if [ -e "$dir/servers/exec" ] && [ -x "$dir/hurd/init" ]; then
-        label="$(count_next_label Hurd)"
-	result "$partition:GNU/Hurd:$label:hurd"
-	exit 0
+	result "$partition:GNU/Hurd:Hurd$((label += 1)):hurd"  # success
+
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
 else
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
+
+done
diff --git a/os-probes/mounted/x86/80minix b/os-probes/mounted/x86/80minix
index e01f669..edb09ed 100755
--- a/os-probes/mounted/x86/80minix
+++ b/os-probes/mounted/x86/80minix
@@ -1,16 +1,14 @@
 #!/bin/sh
 set -e
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
-partition="$1"
-dir="$2"
-type="$3"
+while read partition dir type discard; do
 
 # Weed out stuff that doesn't apply to us
 case "$type" in
 	minix|minix2|ext2) ;;
-	*) exit 1 ;;
+	*) echo "$partition $dir $type $discard"; continue ;;  # failure
 esac
 
 if [ -f "$dir/minix" ] || [ -e "$dir/boot/image_big" ]; then
@@ -20,9 +18,13 @@ if [ -f "$dir/minix" ] || [ -e "$dir/boot/image_big" ]; then
 		boot="chain"
 	fi
 
-	label="$(count_next_label Minix)"
-	result "$partition:Minix:$label:$boot"
-	exit 0
+	result "$partition:Minix:Minix$((label += 1)):$boot"  # success
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
 else
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
+
+done
diff --git a/os-probes/mounted/x86/83haiku b/os-probes/mounted/x86/83haiku
index 6de7a1d..ccd1555 100755
--- a/os-probes/mounted/x86/83haiku
+++ b/os-probes/mounted/x86/83haiku
@@ -1,23 +1,22 @@
 #!/bin/sh
 # Detects Haiku on BeFS partitions.
+set -e
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
-partition="$1"
-mpoint="$2"
-type="$3"
+while read partition dir type discard; do
 
 # Weed out stuff that doesn't apply to us
 case "$type" in
 	befs|befs_be) debug "$partition is a BeFS partition" ;;
-	*) debug "$partition is not a BeFS partition: exiting"; exit 1 ;;
+	*) # debug "$partition is not a BeFS partition"
+	   echo "$partition $dir $type $discard"; continue ;;  # failure
 esac
 
 if head -c 512 "$partition" | grep -qs "system.haiku_loader"; then
 	debug "Stage 1 bootloader found"
 else
-	debug "Stage 1 bootloader not found: exiting"
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
 
 if system="$(item_in_dir "system" "$mpoint")" &&
@@ -26,10 +25,14 @@ if system="$(item_in_dir "system" "$mpoint")" &&
 		item_in_dir -q "kernel_x86_64" "$mpoint/$system")
 then
 	debug "Stage 2 bootloader and kernel found"
-	label="$(count_next_label Haiku)"
-	result "$partition:Haiku:$label:chain"
-	exit 0
+	result "$partition:Haiku:Haiku$((label += 1)):chain"  # success
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
 else
-	debug "Stage 2 bootloader and kernel not found: exiting"
-	exit 1
+	debug "Stage 2 bootloader and kernel not found"
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
+
+done
diff --git a/os-probes/mounted/x86/90solaris b/os-probes/mounted/x86/90solaris
index 0e9148c..00359ec 100755
--- a/os-probes/mounted/x86/90solaris
+++ b/os-probes/mounted/x86/90solaris
@@ -4,16 +4,19 @@
 
 set -e
 
-. /usr/share/os-prober/common.sh
+. $execpfx/common.sh
 
-partition="$1"
-dir="$2"
-type="$3"
+while read partition dir type discard; do
 
 if [ -f "$dir/etc/system" ] && [ -f "$dir/etc/vfstab" ]; then
-        label="$(count_next_label Solaris)"
-	result "$partition:Solaris/IA32:$label:chain"
-	exit 0
+	result "$partition:Solaris/IA32:Solaris$((label += 1)):chain"  # success
+
+	if [ 1 == "$discard" ]; then
+	    umount "$dir"
+	    rmdir "$dir"
+	fi
 else
-	exit 1
+	echo "$partition $dir $type $discard"; continue  # failure
 fi
+
+done

Reply to: