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