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