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>
52 /* how much to bias minor number for a given /dev/<ct#><un#>s<s#> slice */
53 #define SLICE_DELTA (0x10000)
55 static Device *Devices[DEV_MAX];
58 static struct _devname {
62 int major, minor, delta, max;
65 { DEVICE_TYPE_CDROM, "cd%dc", "SCSI CDROM drive", 6, 2, 8, 4, 'b' },
66 { DEVICE_TYPE_CDROM, "mcd%da", "Mitsumi (old model) CDROM drive", 7, 0, 8, 4, 'b' },
67 { DEVICE_TYPE_CDROM, "scd%da", "Sony CDROM drive - CDU31/33A type", 16, 0, 8, 4, 'b' },
69 { DEVICE_TYPE_CDROM, "matcd%da", "Matsushita CDROM ('sound blaster' type)", 17, 0, 8, 4, 'b' },
71 { DEVICE_TYPE_CDROM, "wcd%dc", "ATAPI IDE CDROM", 19, 2, 8, 4, 'b' },
72 { DEVICE_TYPE_CDROM, "acd%dc", "ATAPI/IDE CDROM", 31, 2, 8, 4, 'b' },
73 { DEVICE_TYPE_TAPE, "rsa%d", "SCSI tape drive", 14, 0, 16, 4, 'c' },
74 { DEVICE_TYPE_TAPE, "rwt%d", "Wangtek tape drive", 10, 0, 1, 4, 'c' },
75 { DEVICE_TYPE_DISK, "da%d", "SCSI disk device", 4, 65538, 8, 16, 'b' },
76 { DEVICE_TYPE_DISK, "rda%d", "SCSI disk device", 13, 65538, 8, 16, 'c' },
77 { DEVICE_TYPE_DISK, "wd%d", "IDE/ESDI/MFM/ST506 disk device", 0, 65538, 8, 16, 'b' },
78 { DEVICE_TYPE_DISK, "rwd%d", "IDE/ESDI/MFM/ST506 disk device", 3, 65538, 8, 16, 'c' },
79 { DEVICE_TYPE_DISK, "ad%d", "ATA/IDE disk device", 30, 65538, 8, 16, 'b' },
80 { DEVICE_TYPE_DISK, "rad%d", "ATA/IDE disk device", 116, 65538, 8, 16, 'c' },
81 { DEVICE_TYPE_DISK, "fla%d", "M-Systems DiskOnChip Flash device", 28, 65538, 8, 16, 'b' },
82 { DEVICE_TYPE_DISK, "rfla%d", "M-Systems DiskOnChip Flash devicee", 102, 65538, 8, 16, 'c' },
83 { DEVICE_TYPE_DISK, "wfd%d", "ATAPI floppy device", 1, 65538, 8, 4, 'b' },
84 { DEVICE_TYPE_DISK, "rwfd%d", "ATAPI floppy device", 87, 65538, 8, 4, 'c' },
85 { DEVICE_TYPE_DISK, "afd%d", "ATAPI/IDE floppy device", 32, 65538, 8, 4, 'b' },
86 { DEVICE_TYPE_DISK, "rafd%d", "ATAPI/IDE floppy device", 118, 65538, 8, 4, 'c' },
87 { DEVICE_TYPE_DISK, "mlxd%d", "Mylex RAID disk", 27, 65538, 8, 4, 'b' },
88 { DEVICE_TYPE_DISK, "rmlxd%d", "Mylex RAID disk", 131, 65538, 8, 4, 'c' },
89 { DEVICE_TYPE_DISK, "amrd%d", "AMI MegaRAID drive", 35, 65538, 8, 4, 'b' },
90 { DEVICE_TYPE_DISK, "ramrd%d", "AMI MegaRAID drive", 133, 65538, 8, 4, 'c' },
91 { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 2, 0, 64, 4, 'b' },
92 { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 1, 0, 8, 4, 'b' },
93 { DEVICE_TYPE_FLOPPY, "worm%d", "SCSI optical disk / CDR", 23, 0, 1, 4, 'b' },
94 { DEVICE_TYPE_NETWORK, "al", "ADMtek AL981/AN985 PCI ethernet card" },
95 { DEVICE_TYPE_NETWORK, "ax", "ASIX AX88140A PCI ethernet card" },
96 { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" },
97 { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" },
98 { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" },
99 { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" },
100 { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" },
101 { DEVICE_TYPE_NETWORK, "dm", "Davicom DM9100/DM9102 PCI fast ethernet card" },
102 { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" },
103 { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" },
104 { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" },
105 { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 ethernet card" },
106 { DEVICE_TYPE_NETWORK, "ex", "Intel EtherExpress Pro/10 ethernet card" },
107 { DEVICE_TYPE_NETWORK, "fe", "Fujitsu MB86960A/MB86965A ethernet card" },
108 { DEVICE_TYPE_NETWORK, "ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210" },
109 { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" },
110 { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" },
111 { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
112 { DEVICE_TYPE_NETWORK, "mx", "Macronix 98713/98715/98725 PCI ethernet card" },
113 { DEVICE_TYPE_NETWORK, "pn", "Lite-On 82168/82169 PNIC PCI ethernet card" },
114 { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" },
115 { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" },
116 { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" },
117 { DEVICE_TYPE_NETWORK, "ste", "Sundance ST201 PCI ethernet card" },
118 { DEVICE_TYPE_NETWORK, "sk", "SysKonnect PCI gigabit ethernet card" },
119 { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX ethernet card" },
120 { DEVICE_TYPE_NETWORK, "ti", "Alteon Networks PCI gigabit ethernet card" },
121 { DEVICE_TYPE_NETWORK, "tl", "Texas Instruments ThunderLAN PCI ethernet card" },
122 { DEVICE_TYPE_NETWORK, "vr", "VIA VT3043/VT86C100A Rhine PCI ethernet card" },
123 { DEVICE_TYPE_NETWORK, "vx", "3COM 3c590 / 3c595 ethernet card" },
124 { DEVICE_TYPE_NETWORK, "wb", "Winbond W89C840F PCI ethernet card" },
125 { DEVICE_TYPE_NETWORK, "xl", "3COM 3c90x / 3c90xB PCI ethernet card" },
126 { DEVICE_TYPE_NETWORK, "ze", "IBM/National Semiconductor PCMCIA ethernet card" },
127 { DEVICE_TYPE_NETWORK, "zp", "3Com Etherlink III PCMCIA ethernet card" },
128 { DEVICE_TYPE_NETWORK, "cuaa%d", "%s on device %s (COM%d)", 28, 128, 1, 16, 'c' },
129 { DEVICE_TYPE_NETWORK, "lp", "Parallel Port IP (PLIP) peer connection" },
130 { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
135 new_device(char *name)
139 dev = safe_malloc(sizeof(Device));
140 bzero(dev, sizeof(Device));
142 SAFE_STRCPY(dev->name, name);
146 /* Stubs for unimplemented strategy routines */
148 dummyInit(Device *dev)
154 dummyGet(Device *dev, char *dist, Boolean probe)
160 dummyShutdown(Device *dev)
166 deviceTry(struct _devname dev, char *try, int i)
174 snprintf(unit, sizeof unit, dev.name, i);
175 snprintf(try, FILENAME_MAX, "/dev/%s", unit);
176 fd = open(try, O_RDONLY);
180 if (dev.dev_type == 'c')
184 d = makedev(dev.major, dev.minor + (i * dev.delta));
185 fail = mknod(try, m, d);
186 fd = open(try, O_RDONLY);
191 /* Don't try a "make-under" here since we're using a fixit floppy in this case */
192 snprintf(try, FILENAME_MAX, "/mnt/dev/%s", unit);
193 fd = open(try, O_RDONLY);
197 /* Register a new device in the devices array */
199 deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
200 Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
201 void (*shutdown)(Device *), void *private)
203 Device *newdev = NULL;
205 if (numDevs == DEV_MAX)
206 msgFatal("Too many devices found!");
208 newdev = new_device(name);
209 newdev->description = desc;
210 newdev->devname = devname;
212 newdev->enabled = enabled;
213 newdev->init = init ? init : dummyInit;
214 newdev->get = get ? get : dummyGet;
215 newdev->shutdown = shutdown ? shutdown : dummyShutdown;
216 newdev->private = private;
217 Devices[numDevs] = newdev;
218 Devices[++numDevs] = NULL;
223 /* Reset the registered device chain */
229 for (i = 0; i < numDevs; i++) {
230 Devices[i]->shutdown(Devices[i]);
232 /* XXX this potentially leaks Devices[i]->private if it's being
233 * used to point to something dynamic, but you're not supposed
234 * to call this routine at such times that some open instance
235 * has its private ptr pointing somewhere anyway. XXX
239 Devices[numDevs = 0] = NULL;
242 /* Get all device information for devices we have attached */
248 struct ifreq *ifptr, *end;
250 char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
253 msgNotify("Probing devices, please wait (this can take a while)...");
254 /* First go for the network interfaces. Stolen shamelessly from ifconfig! */
255 ifc.ifc_len = sizeof(buffer);
256 ifc.ifc_buf = buffer;
258 s = socket(AF_INET, SOCK_DGRAM, 0);
260 goto skipif; /* Jump over network iface probing */
262 if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
263 goto skipif; /* Jump over network iface probing */
265 ifflags = ifc.ifc_req->ifr_flags;
266 end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
267 for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
270 /* If it's not a link entry, forget it */
271 if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
274 /* Eliminate network devices that don't make sense */
275 if (!strncmp(ifptr->ifr_name, "lo", 2))
278 /* If we have a slip device, don't register it */
279 if (!strncmp(ifptr->ifr_name, "sl", 2)) {
282 /* And the same for ppp */
283 if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
286 /* Try and find its description */
287 for (i = 0, descr = NULL; device_names[i].name; i++) {
288 int len = strlen(device_names[i].name);
290 if (!ifptr->ifr_name || !ifptr->ifr_name[0])
292 else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
293 descr = device_names[i].description;
298 descr = "<unknown network interface type>";
300 deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
301 mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
302 msgDebug("Found a network device named %s\n", ifptr->ifr_name);
304 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
308 if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
309 ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
313 /* Next, try to find all the types of devices one might need
314 * during the second stage of the installation.
316 for (i = 0; device_names[i].name; i++) {
317 for (j = 0; j < device_names[i].max; j++) {
318 char try[FILENAME_MAX];
320 switch(device_names[i].type) {
321 case DEVICE_TYPE_CDROM:
322 fd = deviceTry(device_names[i], try, j);
323 if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
326 if (fd >= 0) close(fd);
327 snprintf(n, sizeof n, device_names[i].name, j);
328 deviceRegister(strdup(n), device_names[i].description, strdup(try),
329 DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
330 mediaShutdownCDROM, NULL);
331 msgDebug("Found a CDROM device for %s\n", try);
335 case DEVICE_TYPE_TAPE:
336 fd = deviceTry(device_names[i], try, j);
341 snprintf(n, sizeof n, device_names[i].name, j);
342 deviceRegister(strdup(n), device_names[i].description, strdup(try),
343 DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
344 msgDebug("Found a TAPE device for %s\n", try);
348 case DEVICE_TYPE_DISK:
349 fd = deviceTry(device_names[i], try, j);
350 if (fd >= 0 && RunningAsInit) {
354 char unit[80], slice[80];
357 /* Make associated slice entries */
358 for (s = 1; s < 8; s++) {
359 snprintf(unit, sizeof unit, device_names[i].name, j);
360 snprintf(slice, sizeof slice, "/dev/%ss%d", unit, s);
361 d = makedev(device_names[i].major, device_names[i].minor +
362 (j * device_names[i].delta) + (s * SLICE_DELTA));
364 if (device_names[i].dev_type == 'c')
368 fail = mknod(slice, m, d);
369 fd = open(slice, O_RDONLY);
378 case DEVICE_TYPE_FLOPPY:
379 fd = deviceTry(device_names[i], try, j);
384 snprintf(n, sizeof n, device_names[i].name, j);
385 deviceRegister(strdup(n), device_names[i].description, strdup(try),
386 DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
387 mediaShutdownFloppy, NULL);
388 msgDebug("Found a floppy device for %s\n", try);
392 case DEVICE_TYPE_NETWORK:
393 fd = deviceTry(device_names[i], try, j);
394 /* The only network devices that you can open this way are serial ones */
399 cp = device_names[i].description;
400 /* Serial devices get a slip and ppp device each, if supported */
401 newdesc = safe_malloc(strlen(cp) + 40);
402 sprintf(newdesc, cp, "SLIP interface", try, j + 1);
403 deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
404 NULL, mediaShutdownNetwork, NULL);
405 msgDebug("Add mapping for %s to sl0\n", try);
406 newdesc = safe_malloc(strlen(cp) + 50);
407 sprintf(newdesc, cp, "PPP interface", try, j + 1);
408 deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
409 NULL, mediaShutdownNetwork, NULL);
410 msgDebug("Add mapping for %s to ppp0\n", try);
420 /* Finally, go get the disks and look for DOS partitions to register */
421 if ((names = Disk_Names()) != NULL) {
424 for (i = 0; names[i]; i++) {
428 d = Open_Disk(names[i]);
430 msgFatal("Unable to open disk %s", names[i]);
432 deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
433 dummyInit, dummyGet, dummyShutdown, d);
434 msgDebug("Found a disk device named %s\n", names[i]);
436 /* Look for existing DOS partitions to register as "DOS media devices" */
437 for (c1 = d->chunks->part; c1; c1 = c1->next) {
438 if (c1->type == fat || c1->type == extended) {
443 snprintf(devname, sizeof devname, "/dev/%s", c1->name);
444 dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
445 mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
447 msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
455 /* Rescan all devices, after closing previous set - convenience function */
464 * Find all devices that match the criteria, allowing "wildcarding" as well
465 * by allowing NULL or ANY values to match all. The array returned is static
466 * and may be used until the next invocation of deviceFind().
469 deviceFind(char *name, DeviceType class)
471 static Device *found[DEV_MAX];
475 for (i = 0; i < numDevs; i++) {
476 if ((!name || !strcmp(Devices[i]->name, name))
477 && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
478 found[j++] = Devices[i];
481 return j ? found : NULL;
485 deviceFindDescr(char *name, char *desc, DeviceType class)
487 static Device *found[DEV_MAX];
491 for (i = 0; i < numDevs; i++) {
492 if ((!name || !strcmp(Devices[i]->name, name)) &&
493 (!desc || !strcmp(Devices[i]->description, desc)) &&
494 (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
495 found[j++] = Devices[i];
498 return j ? found : NULL;
502 deviceCount(Device **devs)
508 for (i = 0; devs[i]; i++);
513 * Create a menu listing all the devices of a certain type in the system.
514 * The passed-in menu is expected to be a "prototype" from which the new
518 deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
525 devs = deviceFind(NULL, type);
526 numdevs = deviceCount(devs);
529 tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
530 bcopy(menu, tmp, sizeof(DMenu));
531 for (i = 0; devs[i]; i++) {
532 tmp->items[i].prompt = devs[i]->name;
533 for (j = 0; j < numDevs; j++) {
534 if (devs[i] == Devices[j]) {
535 tmp->items[i].title = Devices[j]->description;
540 tmp->items[i].title = "<unknown device type>";
541 tmp->items[i].fire = hook;
542 tmp->items[i].checked = check;
544 tmp->items[i].title = NULL;