1/*
2 * $FreeBSD$
3 *
4 * Copyright (c) 1995
5 *	Jordan Hubbard.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer,
12 *    verbatim and that no modifications are made prior to this
13 *    point in the file.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32#include "sade.h"
33#include <sys/fcntl.h>
34#include <sys/param.h>
35#include <sys/socket.h>
36#include <sys/ioctl.h>
37#include <sys/errno.h>
38#include <sys/time.h>
39#include <sys/stat.h>
40#include <ctype.h>
41#include <libdisk.h>
42
43/* how much to bias minor number for a given /dev/<ct#><un#>s<s#> slice */
44#define SLICE_DELTA	(0x10000)
45
46static Device *Devices[DEV_MAX];
47static int numDevs;
48
49#define	DEVICE_ENTRY(type, name, descr, max)    { type, name, descr, max }
50
51#define	DISK(name, descr, max)                                          \
52	DEVICE_ENTRY(DEVICE_TYPE_DISK, name, descr, max)
53
54static struct _devname {
55    DeviceType type;
56    char *name;
57    char *description;
58    int max;
59} device_names[] = {
60    DISK("da%d",	"SCSI disk device",		16),
61    DISK("ad%d",	"ATA/IDE disk device",		16),
62    DISK("ar%d",	"ATA/IDE RAID device",		16),
63    DISK("afd%d",	"ATAPI/IDE floppy device",	4),
64    DISK("mlxd%d",	"Mylex RAID disk",		4),
65    DISK("amrd%d",	"AMI MegaRAID drive",		4),
66    DISK("idad%d",	"Compaq RAID array",		4),
67    DISK("twed%d",	"3ware ATA RAID array",		4),
68    DISK("aacd%d",	"Adaptec FSA RAID array",	4),
69    DISK("ipsd%d",	"IBM ServeRAID RAID array",	4),
70    DISK("mfid%d",	"LSI MegaRAID SAS array",	4),
71    { 0, NULL, NULL, 0 },
72};
73
74Device *
75new_device(char *name)
76{
77    Device *dev;
78
79    dev = safe_malloc(sizeof(Device));
80    bzero(dev, sizeof(Device));
81    if (name)
82	SAFE_STRCPY(dev->name, name);
83    return dev;
84}
85
86/* Stubs for unimplemented strategy routines */
87Boolean
88dummyInit(Device *dev)
89{
90    return TRUE;
91}
92
93FILE *
94dummyGet(Device *dev, char *dist, Boolean probe)
95{
96    return NULL;
97}
98
99void
100dummyShutdown(Device *dev)
101{
102    return;
103}
104
105static int
106deviceTry(struct _devname dev, char *try, int i)
107{
108    int fd;
109    char unit[80];
110
111    snprintf(unit, sizeof unit, dev.name, i);
112    snprintf(try, FILENAME_MAX, "/dev/%s", unit);
113    if (isDebug())
114	msgDebug("deviceTry: attempting to open %s\n", try);
115    fd = open(try, O_RDONLY);
116    if (fd >= 0) {
117	if (isDebug())
118	    msgDebug("deviceTry: open of %s succeeded.\n", try);
119    }
120    return fd;
121}
122
123/* Register a new device in the devices array */
124Device *
125deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
126	       Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
127	       void (*shutdown)(Device *), void *private)
128{
129    Device *newdev = NULL;
130
131    if (numDevs == DEV_MAX)
132	msgFatal("Too many devices found!");
133    else {
134	newdev = new_device(name);
135	newdev->description = desc;
136	newdev->devname = devname;
137	newdev->type = type;
138	newdev->enabled = enabled;
139	newdev->init = init ? init : dummyInit;
140	newdev->get = get ? get : dummyGet;
141	newdev->shutdown = shutdown ? shutdown : dummyShutdown;
142	newdev->private = private;
143	Devices[numDevs] = newdev;
144	Devices[++numDevs] = NULL;
145    }
146    return newdev;
147}
148
149/* Reset the registered device chain */
150void
151deviceReset(void)
152{
153    int i;
154
155    for (i = 0; i < numDevs; i++) {
156	DEVICE_SHUTDOWN(Devices[i]);
157
158	/* XXX this potentially leaks Devices[i]->private if it's being
159	 * used to point to something dynamic, but you're not supposed
160	 * to call this routine at such times that some open instance
161	 * has its private ptr pointing somewhere anyway. XXX
162	 */
163	free(Devices[i]);
164    }
165    Devices[numDevs = 0] = NULL;
166}
167
168/* Get all device information for devices we have attached */
169void
170deviceGetAll(void)
171{
172    int i, j, fd;
173    char **names;
174
175    msgNotify("Probing devices, please wait (this can take a while)...");
176
177    /* Next, try to find all the types of devices one might need
178     * during the second stage of the installation.
179     */
180    for (i = 0; device_names[i].name; i++) {
181	for (j = 0; j < device_names[i].max; j++) {
182	    char try[FILENAME_MAX];
183
184	    switch(device_names[i].type) {
185	    case DEVICE_TYPE_DISK:
186		fd = deviceTry(device_names[i], try, j);
187		break;
188
189	    default:
190		break;
191	    }
192	}
193    }
194
195    /* Finally, go get the disks and look for DOS partitions to register */
196    if ((names = Disk_Names()) != NULL) {
197	int i;
198
199	for (i = 0; names[i]; i++) {
200	    Disk *d;
201
202	    /* Ignore memory disks */
203	    if (!strncmp(names[i], "md", 2))
204		continue;
205
206	    /*
207	     * XXX
208	     *  Due to unknown reasons, Disk_Names() returns SCSI CDROM as a
209	     * valid disk. This is main reason why sysinstall presents SCSI
210	     * CDROM to available disks in Fdisk/Label menu. In addition,
211	     * adding a blank SCSI CDROM to the menu generates floating point
212	     * exception in sparc64. Disk_Names() just extracts sysctl
213	     * "kern.disks". Why GEOM treats SCSI CDROM as a disk is beyond
214	     * me and that should be investigated.
215	     * For temporary workaround, ignore SCSI CDROM device.
216	     */
217	    if (!strncmp(names[i], "cd", 2))
218		continue;
219
220	    d = Open_Disk(names[i]);
221	    if (!d) {
222		msgDebug("Unable to open disk %s\n", names[i]);
223		continue;
224	    }
225
226	    deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
227			   dummyInit, dummyGet, dummyShutdown, d);
228	    if (isDebug())
229		msgDebug("Found a disk device named %s\n", names[i]);
230
231#if 0
232	    /* Look for existing DOS partitions to register as "DOS media devices" */
233	    for (c1 = d->chunks->part; c1; c1 = c1->next) {
234		if (c1->type == fat || c1->type == efi || c1->type == extended) {
235		    Device *dev;
236		    char devname[80];
237
238		    /* Got one! */
239		    snprintf(devname, sizeof devname, "/dev/%s", c1->name);
240		    dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
241			mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
242		    dev->private = c1;
243		    if (isDebug())
244			msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
245		}
246	    }
247#endif
248	}
249	free(names);
250    }
251    dialog_clear_norefresh();
252}
253
254/* Rescan all devices, after closing previous set - convenience function */
255void
256deviceRescan(void)
257{
258    deviceReset();
259    deviceGetAll();
260}
261
262/*
263 * Find all devices that match the criteria, allowing "wildcarding" as well
264 * by allowing NULL or ANY values to match all.  The array returned is static
265 * and may be used until the next invocation of deviceFind().
266 */
267Device **
268deviceFind(char *name, DeviceType class)
269{
270    static Device *found[DEV_MAX];
271    int i, j;
272
273    j = 0;
274    for (i = 0; i < numDevs; i++) {
275	if ((!name || !strcmp(Devices[i]->name, name))
276	    && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
277	    found[j++] = Devices[i];
278    }
279    found[j] = NULL;
280    return j ? found : NULL;
281}
282
283Device **
284deviceFindDescr(char *name, char *desc, DeviceType class)
285{
286    static Device *found[DEV_MAX];
287    int i, j;
288
289    j = 0;
290    for (i = 0; i < numDevs; i++) {
291	if ((!name || !strcmp(Devices[i]->name, name)) &&
292	    (!desc || !strcmp(Devices[i]->description, desc)) &&
293	    (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
294	    found[j++] = Devices[i];
295    }
296    found[j] = NULL;
297    return j ? found : NULL;
298}
299
300int
301deviceCount(Device **devs)
302{
303    int i;
304
305    if (!devs)
306	return 0;
307    for (i = 0; devs[i]; i++);
308    return i;
309}
310
311/*
312 * Create a menu listing all the devices of a certain type in the system.
313 * The passed-in menu is expected to be a "prototype" from which the new
314 * menu is cloned.
315 */
316DMenu *
317deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
318{
319    Device **devs;
320    int numdevs;
321    DMenu *tmp = NULL;
322    int i, j;
323
324    devs = deviceFind(NULL, type);
325    numdevs = deviceCount(devs);
326    if (!numdevs)
327	return NULL;
328    tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
329    bcopy(menu, tmp, sizeof(DMenu));
330    for (i = 0; devs[i]; i++) {
331	tmp->items[i].prompt = devs[i]->name;
332	for (j = 0; j < numDevs; j++) {
333	    if (devs[i] == Devices[j]) {
334		tmp->items[i].title = Devices[j]->description;
335		break;
336	    }
337	}
338	if (j == numDevs)
339	    tmp->items[i].title = "<unknown device type>";
340	tmp->items[i].fire = hook;
341	tmp->items[i].checked = check;
342    }
343    tmp->items[i].title = NULL;
344    return tmp;
345}
346