device.subr revision 256361
1if [ ! "$_DEVICE_SUBR" ]; then _DEVICE_SUBR=1
2#
3# Copyright (c) 2012-2013 Devin Teske
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD: stable/10/usr.sbin/bsdconfig/share/device.subr 256361 2013-10-11 23:12:05Z dteske $
28#
29############################################################ INCLUDES
30
31BSDCFG_SHARE="/usr/share/bsdconfig"
32. $BSDCFG_SHARE/common.subr || exit 1
33f_dprintf "%s: loading includes..." device.subr
34f_include $BSDCFG_SHARE/dialog.subr
35f_include $BSDCFG_SHARE/strings.subr
36f_include $BSDCFG_SHARE/struct.subr
37
38BSDCFG_LIBE="/usr/libexec/bsdconfig"
39f_include_lang $BSDCFG_LIBE/include/messages.subr
40
41############################################################ GLOBALS
42
43DEVICES=
44DEVICE_NAMES=
45
46# A "device" from sysinstall's point of view
47f_struct_define DEVICE \
48	name		\
49	desc		\
50	devname		\
51	type		\
52	capacity	\
53	enabled		\
54	init		\
55	get		\
56	shutdown	\
57	flags		\
58	private		\
59	volume	
60
61# Network devices have their `private' property set to this
62f_struct_define DEVICE_INFO \
63	use_rtsol use_dhcp ipaddr ipv6addr netmask extras
64
65setvar DEVICE_TYPE_NONE		1
66setvar DEVICE_TYPE_DISK		2
67setvar DEVICE_TYPE_FLOPPY	3
68setvar DEVICE_TYPE_FTP		4
69setvar DEVICE_TYPE_NETWORK	5
70setvar DEVICE_TYPE_CDROM	6
71setvar DEVICE_TYPE_USB		7
72setvar DEVICE_TYPE_DOS		8
73setvar DEVICE_TYPE_UFS		9
74setvar DEVICE_TYPE_NFS		10
75setvar DEVICE_TYPE_ANY		11
76setvar DEVICE_TYPE_HTTP_PROXY	12
77setvar DEVICE_TYPE_HTTP		13
78
79#
80# Default behavior is to call f_device_get_all() automatically when loaded.
81#
82: ${DEVICE_SELF_SCAN_ALL=1}
83
84############################################################ FUNCTIONS
85
86# f_device_try $name [$i [$var_path]]
87#
88# Test a particular device. If $i is given, then $name is expected to contain a
89# single "%d" where $i will be inserted using printf. If $var_path is given,
90# it is used as a variable name to provide the caller the device pathname.
91#
92# Returns success if the device path exists and is a cdev.
93#
94f_device_try()
95{
96	local name="$1" i="$2" var_path="$3" unit
97	if [ "$i" ]; then
98		unit=$( printf "$name" "$i" )
99	else
100		unit="$name"
101	fi
102	case "$unit" in
103	/dev/*) : good ;; # already qualified
104	*) unit="/dev/$unit" ;;
105	esac
106	[ "$var_path" ] && setvar "$var_path" "$unit"
107	f_dprintf "f_device_try: making sure %s is a device node" "$unit"
108	if [ -c "$unit" ]; then
109		f_dprintf "f_device_try: %s is a cdev [good]" "$unit"
110		return $SUCCESS
111	else
112		f_dprintf "f_device_try: %s is not a cdev [skip]" "$unit"
113		return $FAILURE
114	fi
115}
116
117# f_device_register $name $desc $devname $type $enabled $init_function \
118#                   $get_function $shutdown_function $private $capacity
119#
120# Register a device. A `structure' (see struct.subr) is created with the name
121# device_$name (so make sure $name contains only alpha-numeric characters or
122# the underscore, `_'). The remaining arguments after $name correspond to the
123# properties of the `DEVICE' structure-type (defined above).
124#
125# If not already registered, the device is then appended to the DEVICES
126# environment variable, a space-separated list of all registered devices.
127#
128f_device_register()
129{
130	local name="$1" desc="$2" devname="$3" type="$4" enabled="$5"
131	local init_func="$6" get_func="$7" shutdown_func="$8" private="$9"
132	local capacity="${10}"
133
134	f_struct_new DEVICE "device_$name" || return $FAILURE
135	device_$name set name     "$name"
136	device_$name set desc     "$desc"
137	device_$name set devname  "$devname"
138	device_$name set type     "$type"
139	device_$name set enabled  "$enabled"
140	device_$name set init     "$init_func"
141	device_$name set get      "$get_func"
142	device_$name set shutdown "$shutdown_func"
143	device_$name set private  "$private"
144	device_$name set capacity "$capacity"
145
146	# Scan our global register to see if it needs ammending
147	local dev found=
148	for dev in $DEVICES; do
149		[ "$dev" = "$name" ] || continue
150		found=1 && break
151	done
152	[ "$found" ] || DEVICES="$DEVICES $name"
153
154	return $SUCCESS
155}
156
157# f_device_reset
158#
159# Reset the registered device chain.
160#
161f_device_reset()
162{
163	local dev
164	for dev in $DEVICES; do
165		f_device_shutdown $dev
166
167		#
168		# XXX this potentially leaks $dev->private if it's being
169		# used to point to something dynamic, but you're not supposed
170		# to call this routine at such times that some open instance
171		# has its private member pointing somewhere anyway. XXX
172		#
173		f_struct_free device_$dev
174	done
175	DEVICES=
176}
177
178# f_device_get_all
179#
180# Get all device information for devices we have attached.
181#
182f_device_get_all()
183{
184	local devname desc
185
186	f_dprintf "f_device_get_all: Probing devices..."
187	f_dialog_info "$msg_probing_devices_please_wait_this_can_take_a_while"
188
189	# First go for the network interfaces
190	for devname in $( ifconfig -l ); do
191		# Eliminate network devices that don't make sense
192		case "$devname" in
193		lo*) continue ;;
194		esac
195
196		# Try and find its description
197		f_device_desc "$devname" $DEVICE_TYPE_NETWORK desc
198
199		f_dprintf "Found a network device named %s" "$devname"
200		f_device_register $devname \
201			"$desc" "$devname" $DEVICE_TYPE_NETWORK 1 \
202			f_media_init_network "" f_media_shutdown_network "" -1
203	done
204
205	# Next, try to find all the types of devices one might use
206	# as a media source for content
207	#
208
209	local dev desc type max n=0
210	for dev in $DEVICE_NAMES; do
211		n=$(( $n + 1 ))
212		# Get the desc, type, and max (with debugging disabled)
213		# NOTE: Bypassing f_device_name_get() for efficiency
214		# ASIDE: This would be equivalent to the following:
215		# 	debug= f_device_name_get $dev desc
216		# 	debug= f_device_name_get $dev type
217		# 	debug= f_device_name_get $dev max
218		debug= f_getvar _device_desc$n desc
219		debug= f_getvar _device_type$n type
220		debug= f_getvar _device_max$n max
221
222		local k=0
223		while [ $k -lt ${max:-0} ]; do
224			i=$k k=$(( $k + 1 ))
225			devname=""
226			case "$type" in
227			$DEVICE_TYPE_CDROM)
228				f_device_try "$dev" "$i" devname || continue
229				f_device_register "${devname##*/}" "$desc" \
230					"$devname" $DEVICE_TYPE_CDROM 1 \
231					f_media_init_cdrom f_media_get_cdrom \
232					f_media_shutdown_cdrom "" \
233					"$( f_device_capacity "$devname" )"
234				f_dprintf "Found a CDROM device for %s" \
235				          "$devname"
236				;;
237			$DEVICE_TYPE_FLOPPY)
238				f_device_try "$dev" "$i" devname || continue
239				f_device_register "${devname##*/}" "$desc" \
240					"$devname" $DEVICE_TYPE_FLOPPY 1 \
241					f_media_init_floppy \
242					f_media_get_floppy \
243					f_media_shutdown_floppy "" \
244					"$( f_device_capacity "$devname" )"
245				f_dprintf "Found a floppy device for %s" \
246				          "$devname"
247				;;
248			$DEVICE_TYPE_USB)
249				f_device_try "$dev" "$i" devname || continue
250				f_device_register "${devname##*/}" "$desc" \
251					"$devname" $DEVICE_TYPE_USB 1 \
252					f_media_init_usb f_media_get_usb \
253					f_media_shutdown_usb "" \
254					"$( f_device_capacity "$devname" )"
255				f_dprintf "Found a USB disk for %s" "$devname"
256				;;
257			esac
258		done
259	done
260
261	# Register ISO9660 providers as CDROM devices
262	for devname in /dev/iso9660/*; do
263		f_device_try "$devname" || continue
264		f_device_register "${devname##*/}" "ISO9660 file system" \
265			"$devname" $DEVICE_TYPE_CDROM 1 \
266			f_media_init_cdrom f_media_get_cdrom \
267			f_media_shutdown_cdrom "" \
268			"$( f_device_capacity "$devname" )"
269		f_dprintf "Found a CDROM device for %s" "$devname"
270	done
271
272	# Scan for mdconfig(8)-created md(4) devices
273	local filename
274	for devname in /dev/md[0-9] /dev/md[0-9][0-9]; do
275		f_device_try "$devname" || continue
276
277		# See if the md(4) device is a vnode type backed by a file
278		filename=$( sysctl kern.geom.conftxt |
279			awk -v devname="${devname##*/}" \
280			'
281				( $2 == "MD" ) && \
282				( $3 == devname ) && \
283				( $(NF-2) == "vnode" ) && \
284				( $(NF-1) == "file" ) \
285				{
286					print $NF
287				}
288			' )
289		case "$filename" in
290		*.iso) # Register the device as an ISO9660 provider
291			f_device_register "${devname##*/}" \
292				"md(4) vnode file system" \
293				"$devname" $DEVICE_TYPE_CDROM 1 \
294				f_media_init_cdrom f_media_get_cdrom \
295				f_media_shutdown_cdrom "" \
296				"$( f_device_capacity "$devname" )"
297			f_dprintf "Found a CDROM device for %s" "$devname"
298			;;
299		esac
300	done
301
302	# Finally go get the disks and look for partitions to register
303	local diskname slices index type rest slice part
304	for diskname in $( sysctl -n kern.disks ); do
305
306		case "$diskname" in
307		cd*)
308			# XXX
309			#  Due to unknown reasons, kern.disks returns SCSI
310			# CDROM as a valid disk. This will prevent bsdconfig
311			# from presenting SCSI CDROMs as available disks in
312			# various menus. Why GEOM treats SCSI CDROM as a disk
313			# is beyond me and that should be investigated.
314			# For temporary workaround, ignore SCSI CDROM device.
315			#
316			continue ;;
317		esac
318
319		# Try to create a list of partitions and their types,
320		# consisting of "N,typeN ..." (e.g., "1,0xa5 2,0x06").
321		if ! slices=$( fdisk -p "$diskname" 2> /dev/null |
322			awk '( $1 == "p" ) { print $2","$3 }' )
323		then
324			f_dprintf "Unable to open disk %s" "$diskname"
325			continue
326		fi
327
328		# Try and find its description
329		f_device_desc "$diskname" $DEVICE_TYPE_DISK desc
330
331		f_device_register "$diskname" "$desc" \
332		                  "/dev/$diskname" $DEVICE_TYPE_DISK 0 \
333		                  "" "" "" "" \
334		                  "$( f_device_capacity "$diskname" )"
335		f_dprintf "Found a disk device named %s" "$diskname"
336
337		# Look for existing partitions to register
338		for slice in $slices; do
339			index="${slice%%,*}" type="${slice#*,}"
340			slice=${diskname}s$index
341			case "$type" in
342			0x01|0x04|0x06|0x0b|0x0c|0x0e|0xef)
343				# DOS partitions to add as "DOS media devices"
344				f_device_register "$slice" "" \
345					"/dev/$slice" $DEVICE_TYPE_DOS 1 \
346					f_media_init_dos f_media_get_dos \
347					f_media_shutdown_dos "" \
348					"$( f_device_capacity "/dev/$slice" )"
349				f_dprintf "Found a DOS partition %s" "$slice"
350				;;
351			0xa5) # FreeBSD partition
352				for part in $(
353					bsdlabel -r $slice 2> /dev/null |
354						awk -v slice="$slice" '
355						( $1 ~ /[abdefgh]:/ ) {
356							printf "%s%s\n",
357							       slice,
358							       substr($1,1,1)
359						}'
360				); do
361					f_quietly dumpfs -m /dev/$part ||
362						continue
363					f_device_register \
364						"$part" "" "/dev/$part" \
365						$DEVICE_TYPE_UFS 1 \
366						f_media_init_ufs \
367						f_media_get_ufs \
368						f_media_shutdown_ufs "" \
369						"$( f_device_capacity \
370							"$/dev/$part" )"
371					f_dprintf "Found a UFS partition %s" \
372					          "$part"
373				done # parts
374				;;
375			esac
376		done # slices
377
378	done # disks
379}
380
381# f_device_name_get $type $name type|desc|max [$var_to_set]
382#
383# Fetch the device type (type), description (desc), or maximum number of
384# devices to scan for (max) associated with device $name and $type. If $type is
385# either NULL, missing, or set to $DEVICE_TYPE_ANY then only $name is used.
386# Returns success if a match was found, otherwise failure.
387#
388# If $var_to_set is missing or NULL, the device name is printed to standard out
389# for capturing in a sub-shell (which is less-recommended because of
390# performance degredation; for example, when called in a loop).
391#
392f_device_name_get()
393{
394	local __type="$1" __name="$2" __prop="$3" __var_to_set="$4"
395	local __dev __devtype __n=0
396
397	# Return failure if no $name or $prop is an unknown property
398	[ "$__name" ] || return $FAILURE
399	case "$__prop" in type|desc|max) : good ;;
400	*) return $FAILURE; esac
401
402	#
403	# Attempt to create an alternate-form of $__name that contains the
404	# first contiguous string of numbers replaced with `%d' for comparison
405	# against stored pattern names (see MAIN).
406	#
407	local __left="${__name%%[0-9]*}" __right="${__name#*[0-9]}" __dname=
408	if [ "$__left" != "$__name" ]; then
409		# Chop leading digits from right 'til we hit first non-digit
410		while :; do
411			case "$__right" in
412			[0-9]*) __right="${__right#[0-9]}" ;;
413			     *) break
414			esac
415		done
416		__dname="${__left}%d$__right"
417	fi
418
419	[ "$__type" = "$DEVICE_TYPE_ANY" ] && __type=
420	for __dev in $DEVICE_NAMES; do
421		__n=$(( $__n + 1 ))
422		[ "$__dev" = "$__name" -o "$__dev" = "$__dname" ] || continue
423		f_getvar _device_type$__n __devtype
424		[ "${__type:-$__devtype}" = "$__devtype" ] || continue
425		f_getvar _device_$__prop$__n $__var_to_set
426		return $?
427	done
428	return $FAILURE
429}
430
431# f_device_name_set $type $name $desc [$max]
432#
433# Store a description (desc) and [optionally] maximum number of devices to scan
434# for (max) in-association with device $type and $name. Returns success unless
435# $name is NULL or missing. Use the f_device_name_get() routine with the same
436# $name and optionally $type to retrieve one of type, desc, or max properties.
437#
438f_device_name_set()
439{
440	local type="$1" name="$2" desc="$3" max="$4"
441	local dev devtype n=0 found=
442	[ "$name" ] || return $FAILURE
443	for dev in $DEVICE_NAMES; do
444		n=$(( $n + 1 ))
445		[ "$dev" = "$name" ] || continue
446		if f_getvar _device_type$n devtype; then
447			# Allow multiple entries with same name but diff type
448			[ "$devtype" = "$type" ] || continue
449		fi
450		found=1 && break
451	done
452	if [ ! "$found" ]; then
453		DEVICE_NAMES="$DEVICE_NAMES $name"
454		n=$(( $n + 1 ))
455	fi
456	setvar _device_type$n "$type"
457	setvar _device_desc$n "$desc"
458	[ "${4+set}" ] && setvar _device_max$n "$max"
459	return $SUCCESS
460}
461
462# f_device_desc $device_name $device_type [$var_to_set]
463#
464# Print a description for a device name (eg., `fxp0') given a specific device
465# type/class.
466#
467# If $var_to_set is missing or NULL, the device description is printed to
468# standard out for capturing in a sub-shell (which is less-recommended because
469# of performance degredation; for example, when called in a loop).
470#
471f_device_desc()
472{
473	local __name="$1" __type="$2" __var_to_set="$3"
474	local __devname __devunit __cp
475
476	# Check variables
477	[ "$__name" ] || return $SUCCESS
478	[ "$__type" = "$DEVICE_TYPE_ANY" ] && type=
479	[ "$__var_to_set" ] && { setvar "$__var_to_set" "" || return; }
480
481	#
482	# Return sysctl MIB dev.NAME.UNIT.%desc if it exists,
483	# otherwise fall through to below static list.
484	#
485	if f_have sysctl; then
486		__devname="${__name%%[0-9]*}"
487		__devunit="${__name#$__devname}"
488		__devunit="${__devunit%%[!0-9]*}"
489		if [ "$__var_to_set" ]; then
490			if __cp=$(
491				sysctl -n "dev.$__devname.$__devunit.%desc" \
492				2> /dev/null
493			); then
494				setvar "$__var_to_set" "$__cp" &&
495					return $SUCCESS
496			fi
497		else
498			sysctl -n "dev.$__devname.$__devunit.%desc" \
499				2> /dev/null && return $SUCCESS
500		fi
501	fi
502
503	#
504	# For disks, attempt to return camcontrol(8) descriptions.
505	# Otherwise fall through to below static list.
506	#
507	f_have camcontrol &&
508	[ "${__type:-$DEVICE_TYPE_DISK}" = "$DEVICE_TYPE_DISK" ] &&
509	__cp=$( camcontrol devlist 2> /dev/null | awk -v disk="$__name" '
510		$0~"(\\(|,)"disk"(,|\\))" {
511			if (!match($0, "<[^>]+>")) next
512			print substr($0, RSTART+1, RLENGTH-2)
513			found = 1
514			exit
515		}
516		END { exit ! found }
517	' ) && setvar "$__var_to_set" "$__cp" && return $SUCCESS
518
519	#
520	# Attempt to create an alternate-form of $__name that contains the
521	# first contiguous string of numbers replaced with `%d' for comparison
522	# against stored pattern names (see MAIN).
523	#
524	local __left="${__name%%[0-9]*}" __right="${__name#*[0-9]}" __dname=
525	if [ "$__left" != "$__name" ]; then
526		# Chop leading digits from right 'til we hit first non-digit
527		while :; do
528			case "$__right" in
529			[0-9]*) __right="${__right#[0-9]}" ;;
530			     *) break
531			esac
532		done
533		__dname="${__left}%d$__right"
534	fi
535
536	local __dev __devtype __n=0
537	for __dev in $DEVICE_NAMES; do
538		__n=$(( $__n + 1 ))
539		debug= f_getvar _device_type$__n __devtype
540		[ "${__type:-$__devtype}" = "$__devtype" ] || continue
541		if [ "$__devtype" = "$DEVICE_TYPE_NETWORK" ]; then
542			__devname=$( f_substr "$__name" 0 ${#__dev} )
543			[ "$__devname" = "$__dev" ] || continue
544		else
545			[ "$__dev" = "$__name" -o "$__dev" = "$__dname" ] ||
546				continue
547		fi
548		debug= f_getvar _device_desc$__n $__var_to_set
549		return $?
550	done
551
552	#
553	# Sensible fall-backs for specific types
554	#
555	case "$__type" in
556	$DEVICE_TYPE_CDROM)   __cp="<unknown cdrom device type>" ;;
557	$DEVICE_TYPE_DISK)    __cp="<unknown disk device type>" ;;
558	$DEVICE_TYPE_FLOPPY)  __cp="<unknown floppy device type>" ;;
559	$DEVICE_TYPE_USB)     __cp="<unknown usb storage device type>" ;;
560	$DEVICE_TYPE_NETWORK) __cp="<unknown network interface type>" ;;
561	*)
562		__cp="<unknown device type>"
563	esac
564
565	if [ "$__var_to_set" ]; then
566		setvar "$__var_to_set" "$__cp"
567	else
568		echo "$__cp"
569	fi
570
571	return $FAILURE
572}
573
574# f_device_rescan
575#
576# Rescan all devices, after closing previous set - convenience function.
577#
578f_device_rescan()
579{
580	f_device_reset
581	f_device_get_all
582}
583
584# f_device_find $name [$type [$var_to_set]] 
585#
586# Find one or more registered devices by name, type, or both. Returns a space-
587# separated list of devices matching the search criterion.
588#
589# If $var_to_set is missing or NULL, the device name(s) are printed to standard
590# out for capturing in a sub-shell (which is less-recommended because of
591# performance degredation; for example, when called in a loop).
592#
593f_device_find()
594{
595	local __name="$1" __type="${2:-$DEVICE_TYPE_ANY}" __var_to_set="$3"
596	local __dev __devname __devtype __found=
597	for __dev in $DEVICES; do
598		device_$__dev get name __devname
599		device_$__dev get type __devtype
600		if [ "$__name" = "$__devname" -o ! "$__name" ] &&
601		   [ "$__type" = "$DEVICE_TYPE_ANY" -o \
602		     "$__type" = "$__devtype" ]
603		then
604			__found="$__found $__dev"
605		fi
606	done
607	if [ "$__var_to_set" ]; then
608		setvar "$__var_to_set" "${__found# }"
609	else
610		echo $__found
611	fi
612	[ "$__found" ] # Return status
613}
614
615# f_device_init $name
616#
617# Initialize a device by evaluating its `init' function.
618#
619f_device_init()
620{
621	local name="$1" init_func
622	device_$name get init init_func || return $?
623	${init_func:-:} $name
624}
625
626# f_device_get $name $file [$probe]
627#
628# Read $file by evaluating the device's `get' function. The file is commonly
629# produced on standard output (but it truly depends on the function called).
630#
631f_device_get()
632{
633	local name="$1" file="$2" probe="$3" get_func
634	device_$name get get get_func || return $?
635	${get_func:-:} $name "$file" ${3+"$probe"}
636}
637
638# f_device_shutdown $name
639#
640# Shutdown a device by evaluating its `shutdown' function.
641#
642f_device_shutdown()
643{
644	local name="$1" shutdown_func
645	device_$name get shutdown shutdown_func || return $?
646	${shutdown_func:-:} $name
647}
648
649# f_device_menu $title $prompt $hline $device_type [$helpfile]
650#
651# Display a menu listing all the devices of a certain type in the system.
652#
653f_device_menu()
654{
655	f_dialog_title "$1"
656	local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
657	f_dialog_title_restore
658
659	local prompt="$2" hline="$3" type="$4" helpfile="$5"
660
661	local dev devtype devs=
662	for dev in $DEVICES; do
663		device_$dev get type devtype || continue
664		[ "$devtype" = "$type" ] || continue
665		devs="$devs $dev"
666	done
667	[ "$devs" ] || return $DIALOG_CANCEL
668
669	local desc menu_list=
670	for dev in $devs; do
671		device_$dev get desc desc
672		f_shell_escape "$desc" desc
673		menu_list="$menu_list '$dev' '$desc'"
674	done
675
676	local height width rows
677	eval f_dialog_menu_size height width rows \
678	                        \"\$title\"  \
679	                        \"\$btitle\" \
680	                        \"\$prompt\" \
681	                        \"\$hline\"  \
682	                        $menu_list
683
684	local errexit=
685	case $- in *e*) errexit=1; esac
686	set +e
687
688	local mtag
689	while :; do
690		mtag=$( eval $DIALOG \
691			--title \"\$title\"             \
692			--backtitle \"\$btitle\"        \
693			--ok-label \"\$msg_ok\"         \
694			--cancel-label \"\$msg_cancel\" \
695			${helpfile:+                    \
696			  --help-button                 \
697			  --help-label \"\$msg_help\"   \
698			  ${USE_XDIALOG:+--help \"\"}   \
699			}                               \
700			--menu \"\$prompt\"             \
701			$height $width $rows            \
702			$menu_list                      \
703			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
704		)
705		local retval=$?
706
707		[ $retval -ne $DIALOG_HELP ] && break
708			# Otherwise, the Help button was pressed
709		f_show_help "$helpfile"
710			# ...then loop back to menu
711	done
712	f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
713
714	[ "$errexit" ] && set -e
715
716	if [ $retval -eq $DIALOG_OK ]; then
717		# Clean up the output of [X]dialog(1) and return it
718		f_dialog_data_sanitize mtag
719		echo "$mtag" >&2
720	fi
721
722	return $retval
723}
724
725# f_device_capacity $device [$var_to_set]
726#
727# Return the capacity of $device in bytes.
728#
729f_device_capacity()
730{
731	local __dev="$1" __var_to_set="$2"
732	local __bytes
733
734	__bytes=$( diskinfo -v "$__dev" 2> /dev/null |
735		awk '/# mediasize in bytes/{print $1}' ) || __bytes=-1
736
737	if [ "$__var_to_set" ]; then
738		setvar "$__var_to_set" "$__bytes"
739	else
740		echo "$__bytes"
741	fi
742}
743
744#
745# Short-hand
746#
747f_cdrom()   {  f_device_name_set $DEVICE_TYPE_CDROM   "$1" "$2" "$3";  }
748f_disk()    {  f_device_name_set $DEVICE_TYPE_DISK    "$1" "$2" "$3";  }
749f_floppy()  {  f_device_name_set $DEVICE_TYPE_FLOPPY  "$1" "$2" "$3";  }
750f_serial()  {  f_device_name_set $DEVICE_TYPE_NETWORK "$1" "$2" "$3";  }
751f_usb()     {  f_device_name_set $DEVICE_TYPE_USB     "$1" "$2" "$3";  }
752f_network() {  f_device_name_set $DEVICE_TYPE_NETWORK "$1" "$2";       }
753
754############################################################ MAIN
755
756# CDROM, Disk, Floppy, Serial, and USB devices/names
757f_cdrom  "cd%d"   "SCSI CDROM drive"                  4
758f_cdrom  "mcd%d"  "Mitsumi (old model) CDROM drive"   4
759f_cdrom  "scd%d"  "Sony CDROM drive - CDU31/33A type" 4
760f_disk   "aacd%d" "Adaptec FSA RAID array"            4
761f_disk   "ada%d"  "ATA/SATA disk device"              16
762f_disk   "amrd%d" "AMI MegaRAID drive"                4
763f_disk   "da%d"   "SCSI disk device"                  16
764f_disk   "idad%d" "Compaq RAID array"                 4
765f_disk   "ipsd%d" "IBM ServeRAID RAID array"          4
766f_disk   "mfid%d" "LSI MegaRAID SAS array"            4
767f_disk   "mlxd%d" "Mylex RAID disk"                   4
768f_disk   "twed%d" "3ware ATA RAID array"              4
769f_disk   "vtbd%d" "VirtIO Block Device"               16
770f_floppy "fd%d"   "Floppy Drive unit A"               4
771f_serial "cuau%d" "%s on device %s (COM%d)"           16
772f_usb    "da%da"  "USB Mass Storage Device"           16
773
774# Network interfaces/names
775f_network "ae"    "Attansic/Atheros L2 Fast Ethernet"
776f_network "age"   "Attansic/Atheros L1 Gigabit Ethernet"
777f_network "alc"   "Atheros AR8131/AR8132 PCIe Ethernet"
778f_network "ale"   "Atheros AR8121/AR8113/AR8114 PCIe Ethernet"
779f_network "an"    "Aironet 4500/4800 802.11 wireless adapter"
780f_network "ath"   "Atheros IEEE 802.11 wireless adapter"
781f_network "aue"   "ADMtek USB Ethernet adapter"
782f_network "axe"   "ASIX Electronics USB Ethernet adapter"
783f_network "bce"   "Broadcom NetXtreme II Gigabit Ethernet card"
784f_network "bfe"   "Broadcom BCM440x PCI Ethernet card"
785f_network "bge"   "Broadcom BCM570x PCI Gigabit Ethernet card"
786f_network "bm"    "Apple BMAC Built-in Ethernet"
787f_network "bwn"   "Broadcom BCM43xx IEEE 802.11 wireless adapter"
788f_network "cas"   "Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet"
789f_network "cc3i"  "SDL HSSI sync serial PCI card"
790f_network "cue"   "CATC USB Ethernet adapter"
791f_network "cxgb"  "Chelsio T3 10Gb Ethernet card"
792f_network "dc"    "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"
793f_network "de"    "DEC DE435 PCI NIC or other DC21040-AA based card"
794f_network "disc"  "Software discard network interface"
795f_network "ed"    "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"
796f_network "el"    "3Com 3C501 Ethernet card"
797f_network "em"    "Intel(R) PRO/1000 Ethernet card"
798f_network "en"    "Efficient Networks ATM PCI card"
799f_network "ep"    "3Com 3C509 Ethernet card/3C589 PCMCIA"
800f_network "et"    "Agere ET1310 based PCI Express Gigabit Ethernet card"
801f_network "ex"    "Intel EtherExpress Pro/10 Ethernet card"
802f_network "fe"    "Fujitsu MB86960A/MB86965A Ethernet card"
803f_network "fpa"   "DEC DEFPA PCI FDDI card"
804f_network "fwe"   "FireWire Ethernet emulation"
805f_network "fwip"  "IP over FireWire"
806f_network "fxp"   "Intel EtherExpress Pro/100B PCI Fast Ethernet card"
807f_network "gem"   "Apple GMAC or Sun ERI/GEM Ethernet adapter"
808f_network "hme"   "Sun HME (Happy Meal Ethernet) Ethernet adapter"
809f_network "ie"    "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"
810f_network "igb"   "Intel(R) PRO/1000 PCI Express Gigabit Ethernet card"
811f_network "ipw"   "Intel PRO/Wireless 2100 IEEE 802.11 adapter"
812f_network "iwi"   "Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter"
813f_network "iwn"   "Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter"
814f_network "ixgbe" "Intel(R) PRO/10Gb Ethernet card"
815f_network "ixgb"  "Intel(R) PRO/10Gb Ethernet card"
816f_network "ix"    "Intel Etherexpress Ethernet card"
817	# Maintain sequential order of above(3): ixgbe ixgb ix
818f_network "jme"   "JMicron JMC250 Gigabit/JMC260 Fast Ethernet"
819f_network "kue"   "Kawasaki LSI USB Ethernet adapter"
820f_network "le"    "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"
821f_network "lge"   "Level 1 LXT1001 Gigabit Ethernet card"
822f_network "lnc"   "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) Ethernet"
823f_network "lo"    "Loop-back (local) network interface"
824f_network "lp"    "Parallel Port IP (PLIP) peer connection"
825f_network "malo"  "Marvell Libertas 88W8335 802.11 wireless adapter"
826f_network "msk"   "Marvell/SysKonnect Yukon II Gigabit Ethernet"
827f_network "mxge"  "Myricom Myri10GE 10Gb Ethernet card"
828f_network "nfe"   "NVIDIA nForce MCP Ethernet"
829f_network "nge"   "NatSemi PCI Gigabit Ethernet card"
830f_network "ng"    "Vimage netgraph(4) bridged Ethernet device"
831	# Maintain sequential order of above(2): nge ng
832f_network "nve"   "NVIDIA nForce MCP Ethernet"
833f_network "nxge"  "Neterion Xframe 10GbE Server/Storage adapter"
834f_network "pcn"   "AMD Am79c79x PCI Ethernet card"
835f_network "plip"  "Parallel Port IP (PLIP) peer connection"
836f_network "ral"   "Ralink Technology IEEE 802.11 wireless adapter"
837f_network "ray"   "Raytheon Raylink 802.11 wireless adapter"
838f_network "re"    "RealTek 8139C+/8169/8169S/8110S PCI Ethernet adapter"
839f_network "rl"    "RealTek 8129/8139 PCI Ethernet card"
840f_network "rue"   "RealTek USB Ethernet card"
841f_network "rum"   "Ralink Technology USB IEEE 802.11 wireless adapter"
842f_network "sf"    "Adaptec AIC-6915 PCI Ethernet card"
843f_network "sge"   "Silicon Integrated Systems SiS190/191 Ethernet"
844f_network "sis"   "SiS 900/SiS 7016 PCI Ethernet card"
845f_network "sk"    "SysKonnect PCI Gigabit Ethernet card"
846f_network "snc"   "SONIC Ethernet card"
847f_network "sn"    "SMC/Megahertz Ethernet card"
848	# Maintain sequential order of above(2): snc sn
849f_network "sr"    "SDL T1/E1 sync serial PCI card"
850f_network "ste"   "Sundance ST201 PCI Ethernet card"
851f_network "stge"  "Sundance/Tamarack TC9021 Gigabit Ethernet"
852f_network "ti"    "Alteon Networks PCI Gigabit Ethernet card"
853f_network "tl"    "Texas Instruments ThunderLAN PCI Ethernet card"
854f_network "txp"   "3Com 3cR990 Ethernet card"
855f_network "tx"    "SMC 9432TX Ethernet card"
856	# Maintain sequential order of above(2): txp tx
857f_network "uath"  "Atheros AR5005UG and AR5005UX USB wireless adapter"
858f_network "upgt"  "Conexant/Intersil PrismGT USB wireless adapter"
859f_network "ural"  "Ralink Technology RT2500USB 802.11 wireless adapter"
860f_network "urtw"  "Realtek 8187L USB wireless adapter"
861f_network "vge"   "VIA VT612x PCI Gigabit Ethernet card"
862f_network "vlan"  "IEEE 802.1Q VLAN network interface"
863f_network "vr"    "VIA VT3043/VT86C100A Rhine PCI Ethernet card"
864f_network "vx"    "3COM 3c590 / 3c595 Ethernet card"
865f_network "wb"    "Winbond W89C840F PCI Ethernet card"
866f_network "wi"    "Lucent WaveLAN/IEEE 802.11 wireless adapter"
867f_network "wpi"   "Intel 3945ABG IEEE 802.11 wireless adapter"
868f_network "wx"    "Intel Gigabit Ethernet (82452) card"
869f_network "xe"    "Xircom/Intel EtherExpress Pro100/16 Ethernet card"
870f_network "xl"    "3COM 3c90x / 3c90xB PCI Ethernet card"
871f_network "zyd"   "ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter"
872
873f_dprintf "%s: Initialized %u known device names/descriptions." device.subr \
874	  "$( set -- $DEVICE_NAMES; echo $# )"
875
876#
877# Scan for the above devices unless requeted otherwise
878#
879f_dprintf "%s: DEVICE_SELF_SCAN_ALL=[%s]" device.subr "$DEVICE_SELF_SCAN_ALL"
880case "$DEVICE_SELF_SCAN_ALL" in
881""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
882*) f_device_get_all
883esac
884
885f_dprintf "%s: Successfully loaded." device.subr
886
887fi # ! $_DEVICE_SUBR
888