]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/sade/devices.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / sade / devices.c
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
46 static Device *Devices[DEV_MAX];
47 static 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
54 static 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
74 Device *
75 new_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 */
87 Boolean
88 dummyInit(Device *dev)
89 {
90     return TRUE;
91 }
92
93 FILE *
94 dummyGet(Device *dev, char *dist, Boolean probe)
95 {
96     return NULL;
97 }
98
99 void
100 dummyShutdown(Device *dev)
101 {
102     return;
103 }
104
105 static int
106 deviceTry(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 */
124 Device *
125 deviceRegister(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 */
150 void
151 deviceReset(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 */
169 void
170 deviceGetAll(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 */
255 void
256 deviceRescan(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  */
267 Device **
268 deviceFind(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
283 Device **
284 deviceFindDescr(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
300 int
301 deviceCount(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  */
316 DMenu *
317 deviceCreateMenu(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 }