]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - usr.sbin/sysinstall/devices.c
Copy head to stable/8 as part of 8.0 Release cycle.
[FreeBSD/stable/8.git] / usr.sbin / sysinstall / devices.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being essentially a complete rewrite.
6  *
7  * $FreeBSD$
8  *
9  * Copyright (c) 1995
10  *      Jordan Hubbard.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer,
17  *    verbatim and that no modifications are made prior to this
18  *    point in the file.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include "sysinstall.h"
38 #include <sys/fcntl.h>
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/ioctl.h>
42 #include <sys/errno.h>
43 #include <sys/time.h>
44 #include <net/if.h>
45 #include <net/if_var.h>
46 #include <net/if_dl.h>
47 #include <netinet/in.h>
48 #include <netinet/in_var.h>
49 #include <arpa/inet.h>
50 #include <ctype.h>
51 #include <libdisk.h>
52
53 static Device *Devices[DEV_MAX];
54 static int numDevs;
55
56 #define DEVICE_ENTRY(type, name, descr, max)    { type, name, descr, max }
57
58 #define CDROM(name, descr, max)                                 \
59         DEVICE_ENTRY(DEVICE_TYPE_CDROM, name, descr, max)
60 #define DISK(name, descr, max)                                          \
61         DEVICE_ENTRY(DEVICE_TYPE_DISK, name, descr, max)
62 #define FLOPPY(name, descr, max)                                        \
63         DEVICE_ENTRY(DEVICE_TYPE_FLOPPY, name, descr, max)
64 #define NETWORK(name, descr)                                            \
65         DEVICE_ENTRY(DEVICE_TYPE_NETWORK, name, descr, 0)
66 #define SERIAL(name, descr, max)                                        \
67         DEVICE_ENTRY(DEVICE_TYPE_NETWORK, name, descr, max)
68 #define USB(name, descr, max)                                           \
69         DEVICE_ENTRY(DEVICE_TYPE_USB, name, descr, max)
70
71 static struct _devname {
72     DeviceType type;
73     char *name;
74     char *description;
75     int max;
76 } device_names[] = {
77     CDROM("cd%d",       "SCSI CDROM drive",                     4),
78     CDROM("mcd%d",      "Mitsumi (old model) CDROM drive",      4),
79     CDROM("scd%d",      "Sony CDROM drive - CDU31/33A type",    4),
80     CDROM("acd%d",      "ATAPI/IDE CDROM",                      4),
81     DISK("da%d",        "SCSI disk device",                     16),
82     DISK("ad%d",        "ATA/IDE disk device",                  16),
83     DISK("ar%d",        "ATA/IDE RAID device",                  16),
84     DISK("afd%d",       "ATAPI/IDE floppy device",              4),
85     DISK("mlxd%d",      "Mylex RAID disk",                      4),
86     DISK("amrd%d",      "AMI MegaRAID drive",                   4),
87     DISK("idad%d",      "Compaq RAID array",                    4),
88     DISK("twed%d",      "3ware ATA RAID array",                 4),
89     DISK("aacd%d",      "Adaptec FSA RAID array",               4),
90     DISK("ipsd%d",      "IBM ServeRAID RAID array",             4),
91     DISK("mfid%d",      "LSI MegaRAID SAS array",               4),
92     FLOPPY("fd%d",      "floppy drive unit A",                  4),
93     SERIAL("cuad%d",    "%s on device %s (COM%d)",              16),
94     USB("da%da",        "USB Mass Storage Device",              16),
95     NETWORK("ae",       "Attansic/Atheros L2 Fast Ethernet"),
96     NETWORK("age",      "Attansic/Atheros L1 Gigabit Ethernet"),
97     NETWORK("alc",      "Atheros AR8131/AR8132 PCIe Ethernet"),
98     NETWORK("ale",      "Atheros AR8121/AR8113/AR8114 PCIe Ethernet"),
99     NETWORK("an",       "Aironet 4500/4800 802.11 wireless adapter"),
100     NETWORK("ath",      "Atheros IEEE 802.11 wireless adapter"),
101     NETWORK("aue",      "ADMtek USB Ethernet adapter"),
102     NETWORK("axe",      "ASIX Electronics USB Ethernet adapter"),
103     NETWORK("bce",      "Broadcom NetXtreme II Gigabit Ethernet card"),
104     NETWORK("bfe",      "Broadcom BCM440x PCI Ethernet card"),
105     NETWORK("bge",      "Broadcom BCM570x PCI Gigabit Ethernet card"),
106     NETWORK("bm",       "Apple BMAC Built-in Ethernet"),
107     NETWORK("cas",      "Sun Cassini/Cassini+ or NS DP83065 Saturn Ethernet"),
108     NETWORK("cue",      "CATC USB Ethernet adapter"),
109     NETWORK("cxgb",     "Chelsio T3 10Gb Ethernet card"),
110     NETWORK("fpa",      "DEC DEFPA PCI FDDI card"),
111     NETWORK("sr",       "SDL T1/E1 sync serial PCI card"),
112     NETWORK("cc3i",     "SDL HSSI sync serial PCI card"),
113     NETWORK("en",       "Efficient Networks ATM PCI card"),
114     NETWORK("dc",       "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"),
115     NETWORK("de",       "DEC DE435 PCI NIC or other DC21040-AA based card"),
116     NETWORK("fxp",      "Intel EtherExpress Pro/100B PCI Fast Ethernet card"),
117     NETWORK("ed",       "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"),
118     NETWORK("ep",       "3Com 3C509 Ethernet card/3C589 PCMCIA"),
119     NETWORK("em",       "Intel(R) PRO/1000 Ethernet card"),
120     NETWORK("et",       "Agere ET1310 based PCI Express Gigabit Ethernet card"),
121     NETWORK("ex",       "Intel EtherExpress Pro/10 Ethernet card"),
122     NETWORK("fe",       "Fujitsu MB86960A/MB86965A Ethernet card"),
123     NETWORK("gem",      "Apple GMAC or Sun ERI/GEM Ethernet adapter"),
124     NETWORK("hme",      "Sun HME (Happy Meal Ethernet) Ethernet adapter"),
125     NETWORK("ie",       "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"),
126     NETWORK("igb",      "Intel(R) PRO/1000 PCI Express Gigabit Ethernet card"),
127     NETWORK("ipw",      "Intel PRO/Wireless 2100 IEEE 802.11 adapter"),
128     NETWORK("iwi",      "Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter"),
129     NETWORK("iwn",      "Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter"),
130     NETWORK("ixgb",     "Intel(R) PRO/10Gb Ethernet card"),
131     NETWORK("ixgbe",    "Intel(R) PRO/10Gb Ethernet card"),
132     NETWORK("jme",      "JMicron JMC250 Gigabit/JMC260 Fast Ethernet"),
133     NETWORK("kue",      "Kawasaki LSI USB Ethernet adapter"),
134     NETWORK("le",       "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"),
135     NETWORK("lge",      "Level 1 LXT1001 Gigabit Ethernet card"),
136     NETWORK("malo",     "Marvell Libertas 88W8335 802.11 wireless adapter"),
137     NETWORK("msk",      "Marvell/SysKonnect Yukon II Gigabit Ethernet"),
138     NETWORK("mxge",     "Myricom Myri10GE 10Gb Ethernet card"),
139     NETWORK("nfe",      "NVIDIA nForce MCP Ethernet"),
140     NETWORK("nge",      "NatSemi PCI Gigabit Ethernet card"),
141     NETWORK("nve",      "NVIDIA nForce MCP Ethernet"),
142     NETWORK("nxge",     "Neterion Xframe 10GbE Server/Storage adapter"),
143     NETWORK("pcn",      "AMD Am79c79x PCI Ethernet card"),
144     NETWORK("ral",      "Ralink Technology IEEE 802.11 wireless adapter"),
145     NETWORK("ray",      "Raytheon Raylink 802.11 wireless adapter"),
146     NETWORK("re",       "RealTek 8139C+/8169/8169S/8110S PCI Ethernet card"),
147     NETWORK("rl",       "RealTek 8129/8139 PCI Ethernet card"),
148     NETWORK("rue",      "RealTek USB Ethernet card"),
149     NETWORK("rum",      "Ralink Technology USB IEEE 802.11 wireless adapter"),
150     NETWORK("sf",       "Adaptec AIC-6915 PCI Ethernet card"),
151     NETWORK("sis",      "SiS 900/SiS 7016 PCI Ethernet card"),
152 #ifdef PC98
153     NETWORK("snc",      "SONIC Ethernet card"),
154 #endif
155     NETWORK("sn",       "SMC/Megahertz Ethernet card"),
156     NETWORK("ste",      "Sundance ST201 PCI Ethernet card"),
157     NETWORK("stge",     "Sundance/Tamarack TC9021 Gigabit Ethernet"),
158     NETWORK("sk",       "SysKonnect PCI Gigabit Ethernet card"),
159     NETWORK("tx",       "SMC 9432TX Ethernet card"),
160     NETWORK("txp",      "3Com 3cR990 Ethernet card"),
161     NETWORK("ti",       "Alteon Networks PCI Gigabit Ethernet card"),
162     NETWORK("tl",       "Texas Instruments ThunderLAN PCI Ethernet card"),
163     NETWORK("uath",     "Atheros AR5005UG and AR5005UX USB wireless adapter"),
164     NETWORK("upgt",     "Conexant/Intersil PrismGT USB wireless adapter"),
165     NETWORK("ural",     "Ralink Technology RT2500USB 802.11 wireless adapter"),
166     NETWORK("urtw",     "Realtek 8187L USB wireless adapter"),
167     NETWORK("vge",      "VIA VT612x PCI Gigabit Ethernet card"),
168     NETWORK("vr",       "VIA VT3043/VT86C100A Rhine PCI Ethernet card"),
169     NETWORK("vlan",     "IEEE 802.1Q VLAN network interface"),
170     NETWORK("vx",       "3COM 3c590 / 3c595 Ethernet card"),
171     NETWORK("wb",       "Winbond W89C840F PCI Ethernet card"),
172     NETWORK("wi",       "Lucent WaveLAN/IEEE 802.11 wireless adapter"),
173     NETWORK("wpi",      "Intel 3945ABG IEEE 802.11 wireless adapter"),
174     NETWORK("xe",       "Xircom/Intel EtherExpress Pro100/16 Ethernet card"),
175     NETWORK("xl",       "3COM 3c90x / 3c90xB PCI Ethernet card"),
176     NETWORK("zyd",      "ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter"),
177     NETWORK("fwe",      "FireWire Ethernet emulation"),
178     NETWORK("fwip",     "IP over FireWire"),
179     NETWORK("plip",     "Parallel Port IP (PLIP) peer connection"),
180     NETWORK("lo",       "Loop-back (local) network interface"),
181     NETWORK("disc",     "Software discard network interface"),
182     { 0, NULL, NULL, 0 }
183 };
184
185 Device *
186 new_device(char *name)
187 {
188     Device *dev;
189
190     dev = safe_malloc(sizeof(Device));
191     bzero(dev, sizeof(Device));
192     if (name)
193         SAFE_STRCPY(dev->name, name);
194     return dev;
195 }
196
197 /* Stubs for unimplemented strategy routines */
198 Boolean
199 dummyInit(Device *dev)
200 {
201     return TRUE;
202 }
203
204 FILE *
205 dummyGet(Device *dev, char *dist, Boolean probe)
206 {
207     return NULL;
208 }
209
210 void
211 dummyShutdown(Device *dev)
212 {
213     return;
214 }
215
216 static int
217 deviceTry(struct _devname dev, char *try, int i)
218 {
219     int fd;
220     char unit[80];
221
222     snprintf(unit, sizeof unit, dev.name, i);
223     snprintf(try, FILENAME_MAX, "/dev/%s", unit);
224     if (isDebug())
225         msgDebug("deviceTry: attempting to open %s\n", try);
226     fd = open(try, O_RDONLY);
227     if (fd >= 0) {
228         if (isDebug())
229             msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
230     } else {
231         if (isDebug())
232             msgDebug("deviceTry: open of %s failed.\n", try);
233     }
234     return fd;
235 }
236
237 /* Register a new device in the devices array */
238 Device *
239 deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
240                Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
241                void (*shutdown)(Device *), void *private)
242 {
243     Device *newdev = NULL;
244
245     if (numDevs == DEV_MAX)
246         msgFatal("Too many devices found!");
247     else {
248         newdev = new_device(name);
249         newdev->description = desc;
250         newdev->devname = devname;
251         newdev->type = type;
252         newdev->enabled = enabled;
253         newdev->init = init ? init : dummyInit;
254         newdev->get = get ? get : dummyGet;
255         newdev->shutdown = shutdown ? shutdown : dummyShutdown;
256         newdev->private = private;
257         Devices[numDevs] = newdev;
258         Devices[++numDevs] = NULL;
259     }
260     return newdev;
261 }
262
263 /* Reset the registered device chain */
264 void
265 deviceReset(void)
266 {
267     int i;
268
269     for (i = 0; i < numDevs; i++) {
270         DEVICE_SHUTDOWN(Devices[i]);
271
272         /* XXX this potentially leaks Devices[i]->private if it's being
273          * used to point to something dynamic, but you're not supposed
274          * to call this routine at such times that some open instance
275          * has its private ptr pointing somewhere anyway. XXX
276          */
277         free(Devices[i]);
278     }
279     Devices[numDevs = 0] = NULL;
280 }
281
282 /* Get all device information for devices we have attached */
283 void
284 deviceGetAll(void)
285 {
286     int i, j, fd, s;
287     struct ifconf ifc;
288     struct ifreq *ifptr, *end;
289     int ifflags;
290     char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
291     char **names;
292
293     msgNotify("Probing devices, please wait (this can take a while)...");
294     /* First go for the network interfaces.  Stolen shamelessly from ifconfig! */
295     ifc.ifc_len = sizeof(buffer);
296     ifc.ifc_buf = buffer;
297
298     s = socket(AF_INET, SOCK_DGRAM, 0);
299     if (s < 0)
300         goto skipif;    /* Jump over network iface probing */
301
302     if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
303         goto skipif;    /* Jump over network iface probing */
304
305     close(s);
306     ifflags = ifc.ifc_req->ifr_flags;
307     end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
308     for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
309         char *descr;
310
311         /* If it's not a link entry, forget it */
312         if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
313             goto loopend;
314
315         /* Eliminate network devices that don't make sense */
316         if (!strncmp(ifptr->ifr_name, "lo", 2))
317             goto loopend;
318
319         /* If we have a slip device, don't register it */
320         if (!strncmp(ifptr->ifr_name, "sl", 2)) {
321             goto loopend;
322         }
323         /* And the same for ppp */
324         if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
325             goto loopend;
326         }
327         /* Try and find its description */
328         for (i = 0, descr = NULL; device_names[i].name; i++) {
329             int len = strlen(device_names[i].name);
330
331             if (!ifptr->ifr_name || !ifptr->ifr_name[0])
332                 continue;
333             else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
334                 descr = device_names[i].description;
335                 break;
336             }
337         }
338         if (!descr)
339             descr = "<unknown network interface type>";
340
341         deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
342                        mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
343         if (isDebug())
344             msgDebug("Found a network device named %s\n", ifptr->ifr_name);
345         close(s);
346         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
347             continue;
348
349 loopend:
350         if (ifptr->ifr_addr.sa_len)     /* I'm not sure why this is here - it's inherited */
351             ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
352         close(s);
353     }
354
355 skipif:
356     /* Next, try to find all the types of devices one might need
357      * during the second stage of the installation.
358      */
359     for (i = 0; device_names[i].name; i++) {
360         for (j = 0; j < device_names[i].max; j++) {
361             char try[FILENAME_MAX];
362
363             switch(device_names[i].type) {
364             case DEVICE_TYPE_CDROM:
365                 fd = deviceTry(device_names[i], try, j);
366                 if (fd >= 0 || errno == EBUSY) {        /* EBUSY if already mounted */
367                     char n[BUFSIZ];
368
369                     if (fd >= 0) close(fd);
370                     snprintf(n, sizeof n, device_names[i].name, j);
371                     deviceRegister(strdup(n), device_names[i].description, strdup(try),
372                                          DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
373                                          mediaShutdownCDROM, NULL);
374                     if (isDebug())
375                         msgDebug("Found a CDROM device for %s\n", try);
376                 }
377                 break;
378
379             case DEVICE_TYPE_DISK:
380                 /* nothing to do */
381                 break;
382
383             case DEVICE_TYPE_FLOPPY:
384                 fd = deviceTry(device_names[i], try, j);
385                 if (fd >= 0) {
386                     char n[BUFSIZ];
387
388                     close(fd);
389                     snprintf(n, sizeof n, device_names[i].name, j);
390                     deviceRegister(strdup(n), device_names[i].description, strdup(try),
391                                    DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
392                                    mediaShutdownFloppy, NULL);
393                     if (isDebug())
394                         msgDebug("Found a floppy device for %s\n", try);
395                 }
396                 break;
397
398             case DEVICE_TYPE_USB:
399                 fd = deviceTry(device_names[i], try, j);
400                 if (fd >= 0) {
401                         char n[BUFSIZ];
402
403                         close(fd);
404                         snprintf(n, sizeof(n), device_names[i].name, j);
405                         deviceRegister(strdup(n), device_names[i].description,
406                             strdup(try), DEVICE_TYPE_USB, TRUE, mediaInitUSB,
407                             mediaGetUSB, mediaShutdownUSB, NULL);
408
409                         if (isDebug())
410                                 msgDebug("Found a USB disk for %s\n", try);
411                 }
412                 break;
413
414             case DEVICE_TYPE_NETWORK:
415                 fd = deviceTry(device_names[i], try, j);
416                 /* The only network devices that you can open this way are serial ones */
417                 if (fd >= 0) {
418                     char *newdesc, *cp;
419
420                     close(fd);
421                     cp = device_names[i].description;
422                     /* Serial devices get a slip and ppp device each, if supported */
423                     newdesc = safe_malloc(strlen(cp) + 40);
424                     sprintf(newdesc, cp, "SLIP interface", try, j + 1);
425                     deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
426                                    NULL, mediaShutdownNetwork, NULL);
427                     msgDebug("Add mapping for %s to sl0\n", try);
428                     newdesc = safe_malloc(strlen(cp) + 50);
429                     sprintf(newdesc, cp, "PPP interface", try, j + 1);
430                     deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
431                                    NULL, mediaShutdownNetwork, NULL);
432                     if (isDebug())
433                         msgDebug("Add mapping for %s to ppp0\n", try);
434                 }
435                 break;
436
437             default:
438                 break;
439             }
440         }
441     }
442
443     /* Finally, go get the disks and look for partitions to register */
444     if ((names = Disk_Names()) != NULL) {
445         int i;
446
447         for (i = 0; names[i]; i++) {
448             Chunk *c1;
449             Disk *d;
450
451             /* Ignore memory disks */
452             if (!strncmp(names[i], "md", 2))
453                 continue;
454
455             /*
456              * XXX 
457              *  Due to unknown reasons, Disk_Names() returns SCSI CDROM as a
458              * valid disk. This is main reason why sysinstall presents SCSI
459              * CDROM to available disks in Fdisk/Label menu. In addition,
460              * adding a blank SCSI CDROM to the menu generates floating point
461              * exception in sparc64. Disk_Names() just extracts sysctl
462              * "kern.disks". Why GEOM treats SCSI CDROM as a disk is beyond
463              * me and that should be investigated.
464              * For temporary workaround, ignore SCSI CDROM device.
465              */
466             if (!strncmp(names[i], "cd", 2))
467                 continue;
468
469             d = Open_Disk(names[i]);
470             if (!d) {
471                 msgDebug("Unable to open disk %s\n", names[i]);
472                 continue;
473             }
474
475             deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
476                            dummyInit, dummyGet, dummyShutdown, d);
477             if (isDebug())
478                 msgDebug("Found a disk device named %s\n", names[i]);
479
480             /* Look for existing DOS partitions to register as "DOS media devices"
481              * XXX: libdisks handling of extended partitions is too
482              * simplistic - it does not handle them containing (for
483              * example) UFS partitions
484              */
485             for (c1 = d->chunks->part; c1; c1 = c1->next) {
486                 if (c1->type == fat || c1->type == efi || c1->type == extended) {
487                     Device *dev;
488                     char devname[80];
489
490                     /* Got one! */
491                     snprintf(devname, sizeof devname, "/dev/%s", c1->name);
492                     dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
493                                          mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
494                     dev->private = c1;
495                     if (isDebug())
496                         msgDebug("Found a DOS partition %s\n", c1->name);
497                 } else if (c1->type == freebsd) {
498                     Device *dev;
499                     char devname[80];
500                     Chunk *c2;
501                         
502                     for (c2 = c1->part; c2; c2 = c2->next) {
503                         if (c2->type != part || c2->subtype != 7)
504                             continue;
505                         /* Got one! */
506                         snprintf(devname, sizeof devname, "/dev/%s", c1->name);
507                         dev = deviceRegister(c2->name, c2->name, strdup(devname), DEVICE_TYPE_UFS, TRUE,
508                                              mediaInitUFS, mediaGetUFS, mediaShutdownUFS, NULL);
509                         dev->private = c2;
510                         if (isDebug())
511                             msgDebug("Found a UFS sub-partition %s\n", c2->name);
512                     }
513                 }
514                 
515             }
516         }
517         free(names);
518     }
519     dialog_clear_norefresh();
520 }
521
522 /* Rescan all devices, after closing previous set - convenience function */
523 void
524 deviceRescan(void)
525 {
526     deviceReset();
527     deviceGetAll();
528 }
529
530 /*
531  * Find all devices that match the criteria, allowing "wildcarding" as well
532  * by allowing NULL or ANY values to match all.  The array returned is static
533  * and may be used until the next invocation of deviceFind().
534  */
535 Device **
536 deviceFind(char *name, DeviceType class)
537 {
538     static Device *found[DEV_MAX];
539     int i, j;
540
541     j = 0;
542     for (i = 0; i < numDevs; i++) {
543         if ((!name || !strcmp(Devices[i]->name, name))
544             && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
545             found[j++] = Devices[i];
546     }
547     found[j] = NULL;
548     return j ? found : NULL;
549 }
550
551 Device **
552 deviceFindDescr(char *name, char *desc, DeviceType class)
553 {
554     static Device *found[DEV_MAX];
555     int i, j;
556
557     j = 0;
558     for (i = 0; i < numDevs; i++) {
559         if ((!name || !strcmp(Devices[i]->name, name)) &&
560             (!desc || !strcmp(Devices[i]->description, desc)) &&
561             (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
562             found[j++] = Devices[i];
563     }
564     found[j] = NULL;
565     return j ? found : NULL;
566 }
567
568 int
569 deviceCount(Device **devs)
570 {
571     int i;
572
573     if (!devs)
574         return 0;
575     for (i = 0; devs[i]; i++);
576     return i;
577 }
578
579 /*
580  * Create a menu listing all the devices of a certain type in the system.
581  * The passed-in menu is expected to be a "prototype" from which the new
582  * menu is cloned.
583  */
584 DMenu *
585 deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
586 {
587     Device **devs;
588     int numdevs;
589     DMenu *tmp = NULL;
590     int i, j;
591
592     devs = deviceFind(NULL, type);
593     numdevs = deviceCount(devs);
594     if (!numdevs)
595         return NULL;
596     tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
597     bcopy(menu, tmp, sizeof(DMenu));
598     for (i = 0; devs[i]; i++) {
599         tmp->items[i].prompt = devs[i]->name;
600         for (j = 0; j < numDevs; j++) {
601             if (devs[i] == Devices[j]) {
602                 tmp->items[i].title = Devices[j]->description;
603                 break;
604             }
605         }
606         if (j == numDevs)
607             tmp->items[i].title = "<unknown device type>";
608         tmp->items[i].fire = hook;
609         tmp->items[i].checked = check;
610     }
611     tmp->items[i].title = NULL;
612     return tmp;
613 }