]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bsdconfig/share/device.subr
MFC r259054:
[FreeBSD/stable/10.git] / usr.sbin / bsdconfig / share / device.subr
1 if [ ! "$_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
31 BSDCFG_SHARE="/usr/share/bsdconfig"
32 . $BSDCFG_SHARE/common.subr || exit 1
33 f_dprintf "%s: loading includes..." device.subr
34 f_include $BSDCFG_SHARE/dialog.subr
35 f_include $BSDCFG_SHARE/strings.subr
36 f_include $BSDCFG_SHARE/struct.subr
37
38 BSDCFG_LIBE="/usr/libexec/bsdconfig"
39 f_include_lang $BSDCFG_LIBE/include/messages.subr
40
41 ############################################################ GLOBALS
42
43 DEVICES=
44 DEVICE_NAMES=
45 NDEVICES=0
46
47 # A "device" from sysinstall's point of view
48 f_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
63 f_struct_define DEVICE_INFO \
64         use_rtsol use_dhcp ipaddr ipv6addr netmask extras
65
66 setvar DEVICE_TYPE_NONE         1
67 setvar DEVICE_TYPE_DISK         2
68 setvar DEVICE_TYPE_FLOPPY       3
69 setvar DEVICE_TYPE_FTP          4
70 setvar DEVICE_TYPE_NETWORK      5
71 setvar DEVICE_TYPE_CDROM        6
72 setvar DEVICE_TYPE_USB          7
73 setvar DEVICE_TYPE_DOS          8
74 setvar DEVICE_TYPE_UFS          9
75 setvar DEVICE_TYPE_NFS          10
76 setvar DEVICE_TYPE_ANY          11
77 setvar DEVICE_TYPE_HTTP_PROXY   12
78 setvar DEVICE_TYPE_HTTP         13
79
80 # Network devices have the following flags available
81 setvar IF_ETHERNET      1
82 setvar IF_WIRELESS      2
83 setvar 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 #
100 f_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 #
134 f_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 #
167 f_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 #
188 f_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 #
215 f_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 #
406 f_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 #
455 f_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 #
501 f_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 #
534 f_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 #
642 f_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 #
663 f_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 #
684 f_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 #
707 f_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 #
717 f_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 #
732 f_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 #
758 f_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 #
770 f_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 #
781 f_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 #
792 f_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 #
868 f_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 #
886 f_cdrom()   {  f_device_name_set $DEVICE_TYPE_CDROM   "$1" "$2" "$3";  }
887 f_disk()    {  f_device_name_set $DEVICE_TYPE_DISK    "$1" "$2" "$3";  }
888 f_floppy()  {  f_device_name_set $DEVICE_TYPE_FLOPPY  "$1" "$2" "$3";  }
889 f_serial()  {  f_device_name_set $DEVICE_TYPE_NETWORK "$1" "$2" "$3";  }
890 f_usb()     {  f_device_name_set $DEVICE_TYPE_USB     "$1" "$2" "$3";  }
891 f_network() {  f_device_name_set $DEVICE_TYPE_NETWORK "$1" "$2";       }
892
893 ############################################################ MAIN
894
895 # CDROM, Disk, Floppy, Serial, and USB devices/names
896 f_cdrom  "cd%d"   "SCSI CDROM drive"                  4
897 f_cdrom  "mcd%d"  "Mitsumi (old model) CDROM drive"   4
898 f_cdrom  "scd%d"  "Sony CDROM drive - CDU31/33A type" 4
899 f_disk   "aacd%d" "Adaptec FSA RAID array"            4
900 f_disk   "ada%d"  "ATA/SATA disk device"              16
901 f_disk   "amrd%d" "AMI MegaRAID drive"                4
902 f_disk   "da%d"   "SCSI disk device"                  16
903 f_disk   "idad%d" "Compaq RAID array"                 4
904 f_disk   "ipsd%d" "IBM ServeRAID RAID array"          4
905 f_disk   "mfid%d" "LSI MegaRAID SAS array"            4
906 f_disk   "mlxd%d" "Mylex RAID disk"                   4
907 f_disk   "twed%d" "3ware ATA RAID array"              4
908 f_disk   "vtbd%d" "VirtIO Block Device"               16
909 f_floppy "fd%d"   "Floppy Drive unit A"               4
910 f_serial "cuau%d" "%s on device %s (COM%d)"           16
911 f_usb    "da%da"  "USB Mass Storage Device"           16
912
913 # Network interfaces/names
914 f_network "ae"    "Attansic/Atheros L2 Fast Ethernet"
915 f_network "age"   "Attansic/Atheros L1 Gigabit Ethernet"
916 f_network "alc"   "Atheros AR8131/AR8132 PCIe Ethernet"
917 f_network "ale"   "Atheros AR8121/AR8113/AR8114 PCIe Ethernet"
918 f_network "an"    "Aironet 4500/4800 802.11 wireless adapter"
919 f_network "ath"   "Atheros IEEE 802.11 wireless adapter"
920 f_network "aue"   "ADMtek USB Ethernet adapter"
921 f_network "axe"   "ASIX Electronics USB Ethernet adapter"
922 f_network "bce"   "Broadcom NetXtreme II Gigabit Ethernet card"
923 f_network "bfe"   "Broadcom BCM440x PCI Ethernet card"
924 f_network "bge"   "Broadcom BCM570x PCI Gigabit Ethernet card"
925 f_network "bm"    "Apple BMAC Built-in Ethernet"
926 f_network "bwn"   "Broadcom BCM43xx IEEE 802.11 wireless adapter"
927 f_network "cas"   "Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet"
928 f_network "cc3i"  "SDL HSSI sync serial PCI card"
929 f_network "cue"   "CATC USB Ethernet adapter"
930 f_network "cxgb"  "Chelsio T3 10Gb Ethernet card"
931 f_network "dc"    "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"
932 f_network "de"    "DEC DE435 PCI NIC or other DC21040-AA based card"
933 f_network "disc"  "Software discard network interface"
934 f_network "ed"    "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"
935 f_network "el"    "3Com 3C501 Ethernet card"
936 f_network "em"    "Intel(R) PRO/1000 Ethernet card"
937 f_network "en"    "Efficient Networks ATM PCI card"
938 f_network "ep"    "3Com 3C509 Ethernet card/3C589 PCMCIA"
939 f_network "et"    "Agere ET1310 based PCI Express Gigabit Ethernet card"
940 f_network "ex"    "Intel EtherExpress Pro/10 Ethernet card"
941 f_network "fe"    "Fujitsu MB86960A/MB86965A Ethernet card"
942 f_network "fpa"   "DEC DEFPA PCI FDDI card"
943 f_network "fwe"   "FireWire Ethernet emulation"
944 f_network "fwip"  "IP over FireWire"
945 f_network "fxp"   "Intel EtherExpress Pro/100B PCI Fast Ethernet card"
946 f_network "gem"   "Apple GMAC or Sun ERI/GEM Ethernet adapter"
947 f_network "hme"   "Sun HME (Happy Meal Ethernet) Ethernet adapter"
948 f_network "ie"    "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"
949 f_network "igb"   "Intel(R) PRO/1000 PCI Express Gigabit Ethernet card"
950 f_network "ipw"   "Intel PRO/Wireless 2100 IEEE 802.11 adapter"
951 f_network "iwi"   "Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter"
952 f_network "iwn"   "Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter"
953 f_network "ixgbe" "Intel(R) PRO/10Gb Ethernet card"
954 f_network "ixgb"  "Intel(R) PRO/10Gb Ethernet card"
955 f_network "ix"    "Intel Etherexpress Ethernet card"
956         # Maintain sequential order of above(3): ixgbe ixgb ix
957 f_network "jme"   "JMicron JMC250 Gigabit/JMC260 Fast Ethernet"
958 f_network "kue"   "Kawasaki LSI USB Ethernet adapter"
959 f_network "le"    "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"
960 f_network "lge"   "Level 1 LXT1001 Gigabit Ethernet card"
961 f_network "lnc"   "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) Ethernet"
962 f_network "lo"    "Loop-back (local) network interface"
963 f_network "lp"    "Parallel Port IP (PLIP) peer connection"
964 f_network "malo"  "Marvell Libertas 88W8335 802.11 wireless adapter"
965 f_network "msk"   "Marvell/SysKonnect Yukon II Gigabit Ethernet"
966 f_network "mxge"  "Myricom Myri10GE 10Gb Ethernet card"
967 f_network "nfe"   "NVIDIA nForce MCP Ethernet"
968 f_network "nge"   "NatSemi PCI Gigabit Ethernet card"
969 f_network "ng"    "Vimage netgraph(4) bridged Ethernet device"
970         # Maintain sequential order of above(2): nge ng
971 f_network "nve"   "NVIDIA nForce MCP Ethernet"
972 f_network "nxge"  "Neterion Xframe 10GbE Server/Storage adapter"
973 f_network "pcn"   "AMD Am79c79x PCI Ethernet card"
974 f_network "plip"  "Parallel Port IP (PLIP) peer connection"
975 f_network "ral"   "Ralink Technology IEEE 802.11 wireless adapter"
976 f_network "ray"   "Raytheon Raylink 802.11 wireless adapter"
977 f_network "re"    "RealTek 8139C+/8169/8169S/8110S PCI Ethernet adapter"
978 f_network "rl"    "RealTek 8129/8139 PCI Ethernet card"
979 f_network "rue"   "RealTek USB Ethernet card"
980 f_network "rum"   "Ralink Technology USB IEEE 802.11 wireless adapter"
981 f_network "sf"    "Adaptec AIC-6915 PCI Ethernet card"
982 f_network "sge"   "Silicon Integrated Systems SiS190/191 Ethernet"
983 f_network "sis"   "SiS 900/SiS 7016 PCI Ethernet card"
984 f_network "sk"    "SysKonnect PCI Gigabit Ethernet card"
985 f_network "snc"   "SONIC Ethernet card"
986 f_network "sn"    "SMC/Megahertz Ethernet card"
987         # Maintain sequential order of above(2): snc sn
988 f_network "sr"    "SDL T1/E1 sync serial PCI card"
989 f_network "ste"   "Sundance ST201 PCI Ethernet card"
990 f_network "stge"  "Sundance/Tamarack TC9021 Gigabit Ethernet"
991 f_network "ti"    "Alteon Networks PCI Gigabit Ethernet card"
992 f_network "tl"    "Texas Instruments ThunderLAN PCI Ethernet card"
993 f_network "txp"   "3Com 3cR990 Ethernet card"
994 f_network "tx"    "SMC 9432TX Ethernet card"
995         # Maintain sequential order of above(2): txp tx
996 f_network "uath"  "Atheros AR5005UG and AR5005UX USB wireless adapter"
997 f_network "upgt"  "Conexant/Intersil PrismGT USB wireless adapter"
998 f_network "ural"  "Ralink Technology RT2500USB 802.11 wireless adapter"
999 f_network "urtw"  "Realtek 8187L USB wireless adapter"
1000 f_network "vge"   "VIA VT612x PCI Gigabit Ethernet card"
1001 f_network "vlan"  "IEEE 802.1Q VLAN network interface"
1002 f_network "vr"    "VIA VT3043/VT86C100A Rhine PCI Ethernet card"
1003 f_network "vx"    "3COM 3c590 / 3c595 Ethernet card"
1004 f_network "wb"    "Winbond W89C840F PCI Ethernet card"
1005 f_network "wi"    "Lucent WaveLAN/IEEE 802.11 wireless adapter"
1006 f_network "wpi"   "Intel 3945ABG IEEE 802.11 wireless adapter"
1007 f_network "wx"    "Intel Gigabit Ethernet (82452) card"
1008 f_network "xe"    "Xircom/Intel EtherExpress Pro100/16 Ethernet card"
1009 f_network "xl"    "3COM 3c90x / 3c90xB PCI Ethernet card"
1010 f_network "zyd"   "ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter"
1011
1012 f_count NDEVICES $DEVICE_NAMES
1013 f_dprintf "%s: Initialized %u known device names/descriptions." device.subr \
1014           $NDEVICES
1015
1016 #
1017 # Scan for the above devices unless requeted otherwise
1018 #
1019 f_dprintf "%s: DEVICE_SELF_SCAN_ALL=[%s]" device.subr "$DEVICE_SELF_SCAN_ALL"
1020 case "$DEVICE_SELF_SCAN_ALL" in
1021 ""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
1022 *) f_device_get_all
1023 esac
1024
1025 f_dprintf "%s: Successfully loaded." device.subr
1026
1027 fi # ! $_DEVICE_SUBR