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