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