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