2 * The new sysinstall program.
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
10 * Jordan Hubbard. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
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
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.
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
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>
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>
53 static Device *Devices[DEV_MAX];
56 #define DEVICE_ENTRY(type, name, descr, max) { type, name, descr, max }
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)
71 static struct _devname {
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 DISK("mfid%d", "LSI MegaRAID SAS array", 4),
94 FLOPPY("fd%d", "floppy drive unit A", 4),
95 SERIAL("cuad%d", "%s on device %s (COM%d)", 16),
96 NETWORK("an", "Aironet 4500/4800 802.11 wireless adapter"),
97 NETWORK("ath", "Atheros IEEE 802.11 wireless adapter"),
98 NETWORK("aue", "ADMtek USB Ethernet adapter"),
99 NETWORK("axe", "ASIX Electronics USB Ethernet adapter"),
100 NETWORK("bce", "Broadcom NetXtreme II Gigabit Ethernet card"),
101 NETWORK("bfe", "Broadcom BCM440x PCI Ethernet card"),
102 NETWORK("bge", "Broadcom BCM570x PCI Gigabit Ethernet card"),
103 NETWORK("cue", "CATC USB Ethernet adapter"),
104 NETWORK("cxgb", "Chelsio T3 10Gb Ethernet card"),
105 NETWORK("fpa", "DEC DEFPA PCI FDDI card"),
106 NETWORK("sr", "SDL T1/E1 sync serial PCI card"),
107 NETWORK("cc3i", "SDL HSSI sync serial PCI card"),
108 NETWORK("en", "Efficient Networks ATM PCI card"),
109 NETWORK("dc", "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"),
110 NETWORK("de", "DEC DE435 PCI NIC or other DC21040-AA based card"),
111 NETWORK("fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card"),
112 NETWORK("ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"),
113 NETWORK("ep", "3Com 3C509 Ethernet card/3C589 PCMCIA"),
114 NETWORK("em", "Intel(R) PRO/1000 Ethernet card"),
115 NETWORK("ex", "Intel EtherExpress Pro/10 Ethernet card"),
116 NETWORK("fe", "Fujitsu MB86960A/MB86965A Ethernet card"),
117 NETWORK("gem", "Apple GMAC or Sun ERI/GEM Ethernet adapter"),
118 NETWORK("hme", "Sun HME (Happy Meal Ethernet) Ethernet adapter"),
119 NETWORK("ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"),
120 NETWORK("ixgb", "Intel(R) PRO/10Gb Ethernet card"),
121 NETWORK("kue", "Kawasaki LSI USB Ethernet adapter"),
122 NETWORK("le", "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"),
123 NETWORK("lge", "Level 1 LXT1001 Gigabit Ethernet card"),
124 NETWORK("msk", "Marvell/SysKonnect Yukon II Gigabit Ethernet"),
125 NETWORK("mxge", "Myricom Myri10GE 10Gb Ethernet card"),
126 NETWORK("nfe", "NVIDIA nForce MCP Ethernet"),
127 NETWORK("nge", "NatSemi PCI Gigabit Ethernet card"),
128 NETWORK("nve", "NVIDIA nForce MCP Ethernet"),
129 NETWORK("pcn", "AMD Am79c79x PCI Ethernet card"),
130 NETWORK("ray", "Raytheon Raylink 802.11 wireless adapter"),
131 NETWORK("re", "RealTek 8139C+/8169/8169S/8110S PCI Ethernet card"),
132 NETWORK("rl", "RealTek 8129/8139 PCI Ethernet card"),
133 NETWORK("rue", "RealTek USB Ethernet card"),
134 NETWORK("sf", "Adaptec AIC-6915 PCI Ethernet card"),
135 NETWORK("sis", "SiS 900/SiS 7016 PCI Ethernet card"),
137 NETWORK("snc", "SONIC Ethernet card"),
139 NETWORK("sn", "SMC/Megahertz Ethernet card"),
140 NETWORK("ste", "Sundance ST201 PCI Ethernet card"),
141 NETWORK("stge", "Sundance/Tamarack TC9021 Gigabit Ethernet"),
142 NETWORK("sk", "SysKonnect PCI Gigabit Ethernet card"),
143 NETWORK("tx", "SMC 9432TX Ethernet card"),
144 NETWORK("txp", "3Com 3cR990 Ethernet card"),
145 NETWORK("ti", "Alteon Networks PCI Gigabit Ethernet card"),
146 NETWORK("tl", "Texas Instruments ThunderLAN PCI Ethernet card"),
147 NETWORK("vge", "VIA VT612x PCI Gigabit Ethernet card"),
148 NETWORK("vr", "VIA VT3043/VT86C100A Rhine PCI Ethernet card"),
149 NETWORK("vlan", "IEEE 802.1Q VLAN network interface"),
150 NETWORK("vx", "3COM 3c590 / 3c595 Ethernet card"),
151 NETWORK("wb", "Winbond W89C840F PCI Ethernet card"),
152 NETWORK("wi", "Lucent WaveLAN/IEEE 802.11 wireless adapter"),
153 NETWORK("xe", "Xircom/Intel EtherExpress Pro100/16 Ethernet card"),
154 NETWORK("xl", "3COM 3c90x / 3c90xB PCI Ethernet card"),
155 NETWORK("fwe", "FireWire Ethernet emulation"),
156 NETWORK("fwip", "IP over FireWire"),
157 NETWORK("plip", "Parallel Port IP (PLIP) peer connection"),
158 NETWORK("lo", "Loop-back (local) network interface"),
159 NETWORK("disc", "Software discard network interface"),
164 new_device(char *name)
168 dev = safe_malloc(sizeof(Device));
169 bzero(dev, sizeof(Device));
171 SAFE_STRCPY(dev->name, name);
175 /* Stubs for unimplemented strategy routines */
177 dummyInit(Device *dev)
183 dummyGet(Device *dev, char *dist, Boolean probe)
189 dummyShutdown(Device *dev)
195 deviceTry(struct _devname dev, char *try, int i)
200 snprintf(unit, sizeof unit, dev.name, i);
201 snprintf(try, FILENAME_MAX, "/dev/%s", unit);
203 msgDebug("deviceTry: attempting to open %s\n", try);
204 fd = open(try, O_RDONLY);
207 msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
210 msgDebug("deviceTry: open of %s failed.\n", try);
215 /* Register a new device in the devices array */
217 deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
218 Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
219 void (*shutdown)(Device *), void *private)
221 Device *newdev = NULL;
223 if (numDevs == DEV_MAX)
224 msgFatal("Too many devices found!");
226 newdev = new_device(name);
227 newdev->description = desc;
228 newdev->devname = devname;
230 newdev->enabled = enabled;
231 newdev->init = init ? init : dummyInit;
232 newdev->get = get ? get : dummyGet;
233 newdev->shutdown = shutdown ? shutdown : dummyShutdown;
234 newdev->private = private;
235 Devices[numDevs] = newdev;
236 Devices[++numDevs] = NULL;
241 /* Reset the registered device chain */
247 for (i = 0; i < numDevs; i++) {
248 DEVICE_SHUTDOWN(Devices[i]);
250 /* XXX this potentially leaks Devices[i]->private if it's being
251 * used to point to something dynamic, but you're not supposed
252 * to call this routine at such times that some open instance
253 * has its private ptr pointing somewhere anyway. XXX
257 Devices[numDevs = 0] = NULL;
260 /* Get all device information for devices we have attached */
266 struct ifreq *ifptr, *end;
268 char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
271 msgNotify("Probing devices, please wait (this can take a while)...");
272 /* First go for the network interfaces. Stolen shamelessly from ifconfig! */
273 ifc.ifc_len = sizeof(buffer);
274 ifc.ifc_buf = buffer;
276 s = socket(AF_INET, SOCK_DGRAM, 0);
278 goto skipif; /* Jump over network iface probing */
280 if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
281 goto skipif; /* Jump over network iface probing */
284 ifflags = ifc.ifc_req->ifr_flags;
285 end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
286 for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
289 /* If it's not a link entry, forget it */
290 if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
293 /* Eliminate network devices that don't make sense */
294 if (!strncmp(ifptr->ifr_name, "lo", 2))
297 /* If we have a slip device, don't register it */
298 if (!strncmp(ifptr->ifr_name, "sl", 2)) {
301 /* And the same for ppp */
302 if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
305 /* Try and find its description */
306 for (i = 0, descr = NULL; device_names[i].name; i++) {
307 int len = strlen(device_names[i].name);
309 if (!ifptr->ifr_name || !ifptr->ifr_name[0])
311 else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
312 descr = device_names[i].description;
317 descr = "<unknown network interface type>";
319 deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
320 mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
322 msgDebug("Found a network device named %s\n", ifptr->ifr_name);
324 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
328 if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
329 ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
334 /* Next, try to find all the types of devices one might need
335 * during the second stage of the installation.
337 for (i = 0; device_names[i].name; i++) {
338 for (j = 0; j < device_names[i].max; j++) {
339 char try[FILENAME_MAX];
341 switch(device_names[i].type) {
342 case DEVICE_TYPE_CDROM:
343 fd = deviceTry(device_names[i], try, j);
344 if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
347 if (fd >= 0) close(fd);
348 snprintf(n, sizeof n, device_names[i].name, j);
349 deviceRegister(strdup(n), device_names[i].description, strdup(try),
350 DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
351 mediaShutdownCDROM, NULL);
353 msgDebug("Found a CDROM device for %s\n", try);
357 case DEVICE_TYPE_TAPE:
358 fd = deviceTry(device_names[i], try, j);
363 snprintf(n, sizeof n, device_names[i].name, j);
364 deviceRegister(strdup(n), device_names[i].description, strdup(try),
365 DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
367 msgDebug("Found a TAPE device for %s\n", try);
371 case DEVICE_TYPE_DISK:
375 case DEVICE_TYPE_FLOPPY:
376 fd = deviceTry(device_names[i], try, j);
381 snprintf(n, sizeof n, device_names[i].name, j);
382 deviceRegister(strdup(n), device_names[i].description, strdup(try),
383 DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
384 mediaShutdownFloppy, NULL);
386 msgDebug("Found a floppy device for %s\n", try);
390 case DEVICE_TYPE_NETWORK:
391 fd = deviceTry(device_names[i], try, j);
392 /* The only network devices that you can open this way are serial ones */
397 cp = device_names[i].description;
398 /* Serial devices get a slip and ppp device each, if supported */
399 newdesc = safe_malloc(strlen(cp) + 40);
400 sprintf(newdesc, cp, "SLIP interface", try, j + 1);
401 deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
402 NULL, mediaShutdownNetwork, NULL);
403 msgDebug("Add mapping for %s to sl0\n", try);
404 newdesc = safe_malloc(strlen(cp) + 50);
405 sprintf(newdesc, cp, "PPP interface", try, j + 1);
406 deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
407 NULL, mediaShutdownNetwork, NULL);
409 msgDebug("Add mapping for %s to ppp0\n", try);
419 /* Finally, go get the disks and look for DOS partitions to register */
420 if ((names = Disk_Names()) != NULL) {
423 for (i = 0; names[i]; i++) {
427 /* Ignore memory disks */
428 if (!strncmp(names[i], "md", 2))
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.
442 if (!strncmp(names[i], "cd", 2))
445 d = Open_Disk(names[i]);
447 msgDebug("Unable to open disk %s\n", names[i]);
451 deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
452 dummyInit, dummyGet, dummyShutdown, d);
454 msgDebug("Found a disk device named %s\n", names[i]);
456 /* Look for existing DOS partitions to register as "DOS media devices" */
457 for (c1 = d->chunks->part; c1; c1 = c1->next) {
458 if (c1->type == fat || c1->type == efi || c1->type == extended) {
463 snprintf(devname, sizeof devname, "/dev/%s", c1->name);
464 dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
465 mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
468 msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
474 dialog_clear_norefresh();
477 /* Rescan all devices, after closing previous set - convenience function */
486 * Find all devices that match the criteria, allowing "wildcarding" as well
487 * by allowing NULL or ANY values to match all. The array returned is static
488 * and may be used until the next invocation of deviceFind().
491 deviceFind(char *name, DeviceType class)
493 static Device *found[DEV_MAX];
497 for (i = 0; i < numDevs; i++) {
498 if ((!name || !strcmp(Devices[i]->name, name))
499 && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
500 found[j++] = Devices[i];
503 return j ? found : NULL;
507 deviceFindDescr(char *name, char *desc, DeviceType class)
509 static Device *found[DEV_MAX];
513 for (i = 0; i < numDevs; i++) {
514 if ((!name || !strcmp(Devices[i]->name, name)) &&
515 (!desc || !strcmp(Devices[i]->description, desc)) &&
516 (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
517 found[j++] = Devices[i];
520 return j ? found : NULL;
524 deviceCount(Device **devs)
530 for (i = 0; devs[i]; i++);
535 * Create a menu listing all the devices of a certain type in the system.
536 * The passed-in menu is expected to be a "prototype" from which the new
540 deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
547 devs = deviceFind(NULL, type);
548 numdevs = deviceCount(devs);
551 tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
552 bcopy(menu, tmp, sizeof(DMenu));
553 for (i = 0; devs[i]; i++) {
554 tmp->items[i].prompt = devs[i]->name;
555 for (j = 0; j < numDevs; j++) {
556 if (devs[i] == Devices[j]) {
557 tmp->items[i].title = Devices[j]->description;
562 tmp->items[i].title = "<unknown device type>";
563 tmp->items[i].fire = hook;
564 tmp->items[i].checked = check;
566 tmp->items[i].title = NULL;