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("ae", "Attansic/Atheros L2 Fast Ethernet"),
97 NETWORK("age", "Attansic/Atheros L1 Gigabit Ethernet"),
98 NETWORK("ale", "Atheros AR8121/AR8113/AR8114 PCIe Ethernet"),
99 NETWORK("an", "Aironet 4500/4800 802.11 wireless adapter"),
100 NETWORK("ath", "Atheros IEEE 802.11 wireless adapter"),
101 NETWORK("aue", "ADMtek USB Ethernet adapter"),
102 NETWORK("axe", "ASIX Electronics USB Ethernet adapter"),
103 NETWORK("bce", "Broadcom NetXtreme II Gigabit Ethernet card"),
104 NETWORK("bfe", "Broadcom BCM440x PCI Ethernet card"),
105 NETWORK("bge", "Broadcom BCM570x PCI Gigabit Ethernet card"),
106 NETWORK("cue", "CATC USB Ethernet adapter"),
107 NETWORK("cxgb", "Chelsio T3 10Gb Ethernet card"),
108 NETWORK("fpa", "DEC DEFPA PCI FDDI card"),
109 NETWORK("sr", "SDL T1/E1 sync serial PCI card"),
110 NETWORK("cc3i", "SDL HSSI sync serial PCI card"),
111 NETWORK("en", "Efficient Networks ATM PCI card"),
112 NETWORK("dc", "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"),
113 NETWORK("de", "DEC DE435 PCI NIC or other DC21040-AA based card"),
114 NETWORK("fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card"),
115 NETWORK("ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"),
116 NETWORK("ep", "3Com 3C509 Ethernet card/3C589 PCMCIA"),
117 NETWORK("em", "Intel(R) PRO/1000 Ethernet card"),
118 NETWORK("et", "Agere ET1310 based PCI Express Gigabit Ethernet card"),
119 NETWORK("ex", "Intel EtherExpress Pro/10 Ethernet card"),
120 NETWORK("fe", "Fujitsu MB86960A/MB86965A Ethernet card"),
121 NETWORK("gem", "Apple GMAC or Sun ERI/GEM Ethernet adapter"),
122 NETWORK("hme", "Sun HME (Happy Meal Ethernet) Ethernet adapter"),
123 NETWORK("ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"),
124 NETWORK("igb", "Intel(R) PRO/1000 PCI Express Gigabit Ethernet card"),
125 NETWORK("ixgb", "Intel(R) PRO/10Gb Ethernet card"),
126 NETWORK("ixgbe", "Intel(R) PRO/10Gb Ethernet card"),
127 NETWORK("jme", "JMicron JMC250 Gigabit/JMC260 Fast Ethernet"),
128 NETWORK("kue", "Kawasaki LSI USB Ethernet adapter"),
129 NETWORK("le", "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"),
130 NETWORK("lge", "Level 1 LXT1001 Gigabit Ethernet card"),
131 NETWORK("msk", "Marvell/SysKonnect Yukon II Gigabit Ethernet"),
132 NETWORK("mxge", "Myricom Myri10GE 10Gb Ethernet card"),
133 NETWORK("nfe", "NVIDIA nForce MCP Ethernet"),
134 NETWORK("nge", "NatSemi PCI Gigabit Ethernet card"),
135 NETWORK("nve", "NVIDIA nForce MCP Ethernet"),
136 NETWORK("nxge", "Neterion Xframe 10GbE Server/Storage adapter"),
137 NETWORK("pcn", "AMD Am79c79x PCI Ethernet card"),
138 NETWORK("ray", "Raytheon Raylink 802.11 wireless adapter"),
139 NETWORK("re", "RealTek 8139C+/8169/8169S/8110S PCI Ethernet card"),
140 NETWORK("rl", "RealTek 8129/8139 PCI Ethernet card"),
141 NETWORK("rue", "RealTek USB Ethernet card"),
142 NETWORK("sf", "Adaptec AIC-6915 PCI Ethernet card"),
143 NETWORK("sis", "SiS 900/SiS 7016 PCI Ethernet card"),
145 NETWORK("snc", "SONIC Ethernet card"),
147 NETWORK("sn", "SMC/Megahertz Ethernet card"),
148 NETWORK("ste", "Sundance ST201 PCI Ethernet card"),
149 NETWORK("stge", "Sundance/Tamarack TC9021 Gigabit Ethernet"),
150 NETWORK("sk", "SysKonnect PCI Gigabit Ethernet card"),
151 NETWORK("tx", "SMC 9432TX Ethernet card"),
152 NETWORK("txp", "3Com 3cR990 Ethernet card"),
153 NETWORK("ti", "Alteon Networks PCI Gigabit Ethernet card"),
154 NETWORK("tl", "Texas Instruments ThunderLAN PCI Ethernet card"),
155 NETWORK("vge", "VIA VT612x PCI Gigabit Ethernet card"),
156 NETWORK("vr", "VIA VT3043/VT86C100A Rhine PCI Ethernet card"),
157 NETWORK("vlan", "IEEE 802.1Q VLAN network interface"),
158 NETWORK("vx", "3COM 3c590 / 3c595 Ethernet card"),
159 NETWORK("wb", "Winbond W89C840F PCI Ethernet card"),
160 NETWORK("wi", "Lucent WaveLAN/IEEE 802.11 wireless adapter"),
161 NETWORK("xe", "Xircom/Intel EtherExpress Pro100/16 Ethernet card"),
162 NETWORK("xl", "3COM 3c90x / 3c90xB PCI Ethernet card"),
163 NETWORK("fwe", "FireWire Ethernet emulation"),
164 NETWORK("fwip", "IP over FireWire"),
165 NETWORK("plip", "Parallel Port IP (PLIP) peer connection"),
166 NETWORK("lo", "Loop-back (local) network interface"),
167 NETWORK("disc", "Software discard network interface"),
172 new_device(char *name)
176 dev = safe_malloc(sizeof(Device));
177 bzero(dev, sizeof(Device));
179 SAFE_STRCPY(dev->name, name);
183 /* Stubs for unimplemented strategy routines */
185 dummyInit(Device *dev)
191 dummyGet(Device *dev, char *dist, Boolean probe)
197 dummyShutdown(Device *dev)
203 deviceTry(struct _devname dev, char *try, int i)
208 snprintf(unit, sizeof unit, dev.name, i);
209 snprintf(try, FILENAME_MAX, "/dev/%s", unit);
211 msgDebug("deviceTry: attempting to open %s\n", try);
212 fd = open(try, O_RDONLY);
215 msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
218 msgDebug("deviceTry: open of %s failed.\n", try);
223 /* Register a new device in the devices array */
225 deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
226 Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
227 void (*shutdown)(Device *), void *private)
229 Device *newdev = NULL;
231 if (numDevs == DEV_MAX)
232 msgFatal("Too many devices found!");
234 newdev = new_device(name);
235 newdev->description = desc;
236 newdev->devname = devname;
238 newdev->enabled = enabled;
239 newdev->init = init ? init : dummyInit;
240 newdev->get = get ? get : dummyGet;
241 newdev->shutdown = shutdown ? shutdown : dummyShutdown;
242 newdev->private = private;
243 Devices[numDevs] = newdev;
244 Devices[++numDevs] = NULL;
249 /* Reset the registered device chain */
255 for (i = 0; i < numDevs; i++) {
256 DEVICE_SHUTDOWN(Devices[i]);
258 /* XXX this potentially leaks Devices[i]->private if it's being
259 * used to point to something dynamic, but you're not supposed
260 * to call this routine at such times that some open instance
261 * has its private ptr pointing somewhere anyway. XXX
265 Devices[numDevs = 0] = NULL;
268 /* Get all device information for devices we have attached */
274 struct ifreq *ifptr, *end;
276 char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
279 msgNotify("Probing devices, please wait (this can take a while)...");
280 /* First go for the network interfaces. Stolen shamelessly from ifconfig! */
281 ifc.ifc_len = sizeof(buffer);
282 ifc.ifc_buf = buffer;
284 s = socket(AF_INET, SOCK_DGRAM, 0);
286 goto skipif; /* Jump over network iface probing */
288 if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
289 goto skipif; /* Jump over network iface probing */
292 ifflags = ifc.ifc_req->ifr_flags;
293 end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
294 for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
297 /* If it's not a link entry, forget it */
298 if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
301 /* Eliminate network devices that don't make sense */
302 if (!strncmp(ifptr->ifr_name, "lo", 2))
305 /* If we have a slip device, don't register it */
306 if (!strncmp(ifptr->ifr_name, "sl", 2)) {
309 /* And the same for ppp */
310 if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
313 /* Try and find its description */
314 for (i = 0, descr = NULL; device_names[i].name; i++) {
315 int len = strlen(device_names[i].name);
317 if (!ifptr->ifr_name || !ifptr->ifr_name[0])
319 else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
320 descr = device_names[i].description;
325 descr = "<unknown network interface type>";
327 deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
328 mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
330 msgDebug("Found a network device named %s\n", ifptr->ifr_name);
332 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
336 if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
337 ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
342 /* Next, try to find all the types of devices one might need
343 * during the second stage of the installation.
345 for (i = 0; device_names[i].name; i++) {
346 for (j = 0; j < device_names[i].max; j++) {
347 char try[FILENAME_MAX];
349 switch(device_names[i].type) {
350 case DEVICE_TYPE_CDROM:
351 fd = deviceTry(device_names[i], try, j);
352 if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
355 if (fd >= 0) close(fd);
356 snprintf(n, sizeof n, device_names[i].name, j);
357 deviceRegister(strdup(n), device_names[i].description, strdup(try),
358 DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
359 mediaShutdownCDROM, NULL);
361 msgDebug("Found a CDROM device for %s\n", try);
365 case DEVICE_TYPE_TAPE:
366 fd = deviceTry(device_names[i], try, j);
371 snprintf(n, sizeof n, device_names[i].name, j);
372 deviceRegister(strdup(n), device_names[i].description, strdup(try),
373 DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
375 msgDebug("Found a TAPE device for %s\n", try);
379 case DEVICE_TYPE_DISK:
383 case DEVICE_TYPE_FLOPPY:
384 fd = deviceTry(device_names[i], try, j);
389 snprintf(n, sizeof n, device_names[i].name, j);
390 deviceRegister(strdup(n), device_names[i].description, strdup(try),
391 DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
392 mediaShutdownFloppy, NULL);
394 msgDebug("Found a floppy device for %s\n", try);
398 case DEVICE_TYPE_NETWORK:
399 fd = deviceTry(device_names[i], try, j);
400 /* The only network devices that you can open this way are serial ones */
405 cp = device_names[i].description;
406 /* Serial devices get a slip and ppp device each, if supported */
407 newdesc = safe_malloc(strlen(cp) + 40);
408 sprintf(newdesc, cp, "SLIP interface", try, j + 1);
409 deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
410 NULL, mediaShutdownNetwork, NULL);
411 msgDebug("Add mapping for %s to sl0\n", try);
412 newdesc = safe_malloc(strlen(cp) + 50);
413 sprintf(newdesc, cp, "PPP interface", try, j + 1);
414 deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
415 NULL, mediaShutdownNetwork, NULL);
417 msgDebug("Add mapping for %s to ppp0\n", try);
427 /* Finally, go get the disks and look for DOS partitions to register */
428 if ((names = Disk_Names()) != NULL) {
431 for (i = 0; names[i]; i++) {
435 /* Ignore memory disks */
436 if (!strncmp(names[i], "md", 2))
441 * Due to unknown reasons, Disk_Names() returns SCSI CDROM as a
442 * valid disk. This is main reason why sysinstall presents SCSI
443 * CDROM to available disks in Fdisk/Label menu. In addition,
444 * adding a blank SCSI CDROM to the menu generates floating point
445 * exception in sparc64. Disk_Names() just extracts sysctl
446 * "kern.disks". Why GEOM treats SCSI CDROM as a disk is beyond
447 * me and that should be investigated.
448 * For temporary workaround, ignore SCSI CDROM device.
450 if (!strncmp(names[i], "cd", 2))
453 d = Open_Disk(names[i]);
455 msgDebug("Unable to open disk %s\n", names[i]);
459 deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
460 dummyInit, dummyGet, dummyShutdown, d);
462 msgDebug("Found a disk device named %s\n", names[i]);
464 /* Look for existing DOS partitions to register as "DOS media devices" */
465 for (c1 = d->chunks->part; c1; c1 = c1->next) {
466 if (c1->type == fat || c1->type == efi || c1->type == extended) {
471 snprintf(devname, sizeof devname, "/dev/%s", c1->name);
472 dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
473 mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
476 msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
482 dialog_clear_norefresh();
485 /* Rescan all devices, after closing previous set - convenience function */
494 * Find all devices that match the criteria, allowing "wildcarding" as well
495 * by allowing NULL or ANY values to match all. The array returned is static
496 * and may be used until the next invocation of deviceFind().
499 deviceFind(char *name, DeviceType class)
501 static Device *found[DEV_MAX];
505 for (i = 0; i < numDevs; i++) {
506 if ((!name || !strcmp(Devices[i]->name, name))
507 && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
508 found[j++] = Devices[i];
511 return j ? found : NULL;
515 deviceFindDescr(char *name, char *desc, DeviceType class)
517 static Device *found[DEV_MAX];
521 for (i = 0; i < numDevs; i++) {
522 if ((!name || !strcmp(Devices[i]->name, name)) &&
523 (!desc || !strcmp(Devices[i]->description, desc)) &&
524 (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
525 found[j++] = Devices[i];
528 return j ? found : NULL;
532 deviceCount(Device **devs)
538 for (i = 0; devs[i]; i++);
543 * Create a menu listing all the devices of a certain type in the system.
544 * The passed-in menu is expected to be a "prototype" from which the new
548 deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
555 devs = deviceFind(NULL, type);
556 numdevs = deviceCount(devs);
559 tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
560 bcopy(menu, tmp, sizeof(DMenu));
561 for (i = 0; devs[i]; i++) {
562 tmp->items[i].prompt = devs[i]->name;
563 for (j = 0; j < numDevs; j++) {
564 if (devs[i] == Devices[j]) {
565 tmp->items[i].title = Devices[j]->description;
570 tmp->items[i].title = "<unknown device type>";
571 tmp->items[i].fire = hook;
572 tmp->items[i].checked = check;
574 tmp->items[i].title = NULL;