2008-09-03 Robert Millan <rmh@aybabtu.com>
[grub2/phcoder/solaris.git] / util / grub-mkdevicemap.c
blob38a90b7b3a1968ebacde75e323f2072e08f737f8
1 /* grub-mkdevicemap.c - make a device map file automatically */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <config.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
32 #include <grub/util/misc.h>
34 #define _GNU_SOURCE 1
35 #include <getopt.h>
37 #ifdef __linux__
38 # if !defined(__GLIBC__) || \
39 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
40 /* Maybe libc doesn't have large file support. */
41 # include <linux/unistd.h> /* _llseek */
42 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
43 # include <sys/ioctl.h> /* ioctl */
44 # ifndef HDIO_GETGEO
45 # define HDIO_GETGEO 0x0301 /* get device geometry */
46 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
47 defined. */
48 struct hd_geometry
50 unsigned char heads;
51 unsigned char sectors;
52 unsigned short cylinders;
53 unsigned long start;
55 # endif /* ! HDIO_GETGEO */
56 # ifndef FLOPPY_MAJOR
57 # define FLOPPY_MAJOR 2 /* the major number for floppy */
58 # endif /* ! FLOPPY_MAJOR */
59 # ifndef MAJOR
60 # define MAJOR(dev) \
61 ({ \
62 unsigned long long __dev = (dev); \
63 (unsigned) ((__dev >> 8) & 0xfff) \
64 | ((unsigned int) (__dev >> 32) & ~0xfff); \
66 # endif /* ! MAJOR */
67 # ifndef CDROM_GET_CAPABILITY
68 # define CDROM_GET_CAPABILITY 0x5331 /* get capabilities */
69 # endif /* ! CDROM_GET_CAPABILITY */
70 # ifndef BLKGETSIZE
71 # define BLKGETSIZE _IO(0x12,96) /* return device size */
72 # endif /* ! BLKGETSIZE */
73 #endif /* __linux__ */
75 /* Use __FreeBSD_kernel__ instead of __FreeBSD__ for compatibility with
76 kFreeBSD-based non-FreeBSD systems (e.g. GNU/kFreeBSD) */
77 #if defined(__FreeBSD__) && ! defined(__FreeBSD_kernel__)
78 # define __FreeBSD_kernel__
79 #endif
80 #ifdef __FreeBSD_kernel__
81 /* Obtain version of kFreeBSD headers */
82 # include <osreldate.h>
83 # ifndef __FreeBSD_kernel_version
84 # define __FreeBSD_kernel_version __FreeBSD_version
85 # endif
87 /* Runtime detection of kernel */
88 # include <sys/utsname.h>
89 int
90 get_kfreebsd_version (void)
92 struct utsname uts;
93 int major;
94 int minor;
95 int v[2];
97 uname (&uts);
98 sscanf (uts.release, "%d.%d", &major, &minor);
100 if (major >= 9)
101 major = 9;
102 if (major >= 5)
104 v[0] = minor/10; v[1] = minor%10;
106 else
108 v[0] = minor%10; v[1] = minor/10;
110 return major*100000+v[0]*10000+v[1]*1000;
112 #endif /* __FreeBSD_kernel__ */
114 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
115 # include <sys/ioctl.h> /* ioctl */
116 # include <sys/disklabel.h>
117 # include <sys/cdio.h> /* CDIOCCLRDEBUG */
118 # if defined(__FreeBSD_kernel__)
119 # include <sys/param.h>
120 # if __FreeBSD_kernel_version >= 500040
121 # include <sys/disk.h>
122 # endif
123 # endif /* __FreeBSD_kernel__ */
124 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
126 #ifdef HAVE_OPENDISK
127 # include <util.h>
128 #endif /* HAVE_OPENDISK */
130 #ifdef __linux__
131 /* Check if we have devfs support. */
132 static int
133 have_devfs (void)
135 struct stat st;
136 return stat ("/dev/.devfsd", &st) == 0;
138 #endif /* __linux__ */
140 /* These three functions are quite different among OSes. */
141 static void
142 get_floppy_disk_name (char *name, int unit)
144 #if defined(__linux__)
145 /* GNU/Linux */
146 if (have_devfs ())
147 sprintf (name, "/dev/floppy/%d", unit);
148 else
149 sprintf (name, "/dev/fd%d", unit);
150 #elif defined(__GNU__)
151 /* GNU/Hurd */
152 sprintf (name, "/dev/fd%d", unit);
153 #elif defined(__FreeBSD_kernel__)
154 /* kFreeBSD */
155 if (get_kfreebsd_version () >= 400000)
156 sprintf (name, "/dev/fd%d", unit);
157 else
158 sprintf (name, "/dev/rfd%d", unit);
159 #elif defined(__NetBSD__)
160 /* NetBSD */
161 /* opendisk() doesn't work for floppies. */
162 sprintf (name, "/dev/rfd%da", unit);
163 #elif defined(__OpenBSD__)
164 /* OpenBSD */
165 sprintf (name, "/dev/rfd%dc", unit);
166 #elif defined(__QNXNTO__)
167 /* QNX RTP */
168 sprintf (name, "/dev/fd%d", unit);
169 #elif defined(__CYGWIN__)
170 /* Cygwin */
171 sprintf (name, "/dev/fd%d", unit);
172 #elif defined(__MINGW32__)
173 (void) unit;
174 *name = 0;
175 #else
176 # warning "BIOS floppy drives cannot be guessed in your operating system."
177 /* Set NAME to a bogus string. */
178 *name = 0;
179 #endif
182 static void
183 get_ide_disk_name (char *name, int unit)
185 #if defined(__linux__)
186 /* GNU/Linux */
187 sprintf (name, "/dev/hd%c", unit + 'a');
188 #elif defined(__GNU__)
189 /* GNU/Hurd */
190 sprintf (name, "/dev/hd%d", unit);
191 #elif defined(__FreeBSD_kernel__)
192 /* kFreeBSD */
193 if (get_kfreebsd_version () >= 400000)
194 sprintf (name, "/dev/ad%d", unit);
195 else
196 sprintf (name, "/dev/rwd%d", unit);
197 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
198 /* NetBSD */
199 char shortname[16];
200 int fd;
202 sprintf (shortname, "wd%d", unit);
203 fd = opendisk (shortname, O_RDONLY, name,
204 16, /* length of NAME */
205 0 /* char device */
207 close (fd);
208 #elif defined(__OpenBSD__)
209 /* OpenBSD */
210 sprintf (name, "/dev/rwd%dc", unit);
211 #elif defined(__QNXNTO__)
212 /* QNX RTP */
213 /* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could
214 contain SCSI disks. */
215 sprintf (name, "/dev/hd%d", unit);
216 #elif defined(__CYGWIN__)
217 /* Cygwin emulates all disks as /dev/sdX. */
218 (void) unit;
219 *name = 0;
220 #elif defined(__MINGW32__)
221 sprintf (name, "//./PHYSICALDRIVE%d", unit);
222 #else
223 # warning "BIOS IDE drives cannot be guessed in your operating system."
224 /* Set NAME to a bogus string. */
225 *name = 0;
226 #endif
229 static void
230 get_scsi_disk_name (char *name, int unit)
232 #if defined(__linux__)
233 /* GNU/Linux */
234 sprintf (name, "/dev/sd%c", unit + 'a');
235 #elif defined(__GNU__)
236 /* GNU/Hurd */
237 sprintf (name, "/dev/sd%d", unit);
238 #elif defined(__FreeBSD_kernel__)
239 /* kFreeBSD */
240 if (get_kfreebsd_version () >= 400000)
241 sprintf (name, "/dev/da%d", unit);
242 else
243 sprintf (name, "/dev/rda%d", unit);
244 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
245 /* NetBSD */
246 char shortname[16];
247 int fd;
249 sprintf (shortname, "sd%d", unit);
250 fd = opendisk (shortname, O_RDONLY, name,
251 16, /* length of NAME */
252 0 /* char device */
254 close (fd);
255 #elif defined(__OpenBSD__)
256 /* OpenBSD */
257 sprintf (name, "/dev/rsd%dc", unit);
258 #elif defined(__QNXNTO__)
259 /* QNX RTP */
260 /* QNX RTP doesn't distinguish SCSI from IDE, so it is better to
261 disable the detection of SCSI disks here. */
262 *name = 0;
263 #elif defined(__CYGWIN__)
264 /* Cygwin emulates all disks as /dev/sdX. */
265 sprintf (name, "/dev/sd%c", unit + 'a');
266 #elif defined(__MINGW32__)
267 (void) unit;
268 *name = 0;
269 #else
270 # warning "BIOS SCSI drives cannot be guessed in your operating system."
271 /* Set NAME to a bogus string. */
272 *name = 0;
273 #endif
276 #ifdef __linux__
277 static void
278 get_virtio_disk_name (char *name, int unit)
280 sprintf (name, "/dev/vd%c", unit + 'a');
283 static void
284 get_dac960_disk_name (char *name, int controller, int drive)
286 sprintf (name, "/dev/rd/c%dd%d", controller, drive);
289 static void
290 get_ataraid_disk_name (char *name, int unit)
292 sprintf (name, "/dev/ataraid/d%c", unit + '0');
295 static void
296 get_i2o_disk_name (char *name, char unit)
298 sprintf (name, "/dev/i2o/hd%c", unit);
301 static void
302 get_cciss_disk_name (char *name, int controller, int drive)
304 sprintf (name, "/dev/cciss/c%dd%d", controller, drive);
307 static void
308 get_ida_disk_name (char *name, int controller, int drive)
310 sprintf (name, "/dev/ida/c%dd%d", controller, drive);
313 static void
314 get_mmc_disk_name (char *name, int unit)
316 sprintf (name, "/dev/mmcblk%d", unit);
319 static void
320 get_xvd_disk_name (char *name, int unit)
322 sprintf (name, "/dev/xvd%c", unit + 'a');
324 #endif
326 /* Check if DEVICE can be read. If an error occurs, return zero,
327 otherwise return non-zero. */
328 static int
329 check_device (const char *device)
331 char buf[512];
332 FILE *fp;
334 /* If DEVICE is empty, just return error. */
335 if (*device == 0)
336 return 0;
338 fp = fopen (device, "r");
339 if (! fp)
341 switch (errno)
343 #ifdef ENOMEDIUM
344 case ENOMEDIUM:
345 # if 0
346 /* At the moment, this finds only CDROMs, which can't be
347 read anyway, so leave it out. Code should be
348 reactivated if `removable disks' and CDROMs are
349 supported. */
350 /* Accept it, it may be inserted. */
351 return 1;
352 # endif
353 break;
354 #endif /* ENOMEDIUM */
355 default:
356 /* Break case and leave. */
357 break;
359 /* Error opening the device. */
360 return 0;
363 /* Make sure CD-ROMs don't get assigned a BIOS disk number
364 before SCSI disks! */
365 #ifdef __linux__
366 # ifdef CDROM_GET_CAPABILITY
367 if (ioctl (fileno (fp), CDROM_GET_CAPABILITY, 0) >= 0)
368 return 0;
369 # else /* ! CDROM_GET_CAPABILITY */
370 /* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl. */
372 struct hd_geometry hdg;
373 struct stat st;
375 if (fstat (fileno (fp), &st))
376 return 0;
378 /* If it is a block device and isn't a floppy, check if HDIO_GETGEO
379 succeeds. */
380 if (S_ISBLK (st.st_mode)
381 && MAJOR (st.st_rdev) != FLOPPY_MAJOR
382 && ioctl (fileno (fp), HDIO_GETGEO, &hdg))
383 return 0;
385 # endif /* ! CDROM_GET_CAPABILITY */
386 #endif /* __linux__ */
388 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
389 # ifdef CDIOCCLRDEBUG
390 if (ioctl (fileno (fp), CDIOCCLRDEBUG, 0) >= 0)
391 return 0;
392 # endif /* CDIOCCLRDEBUG */
393 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
395 /* Attempt to read the first sector. */
396 if (fread (buf, 1, 512, fp) != 512)
398 fclose (fp);
399 return 0;
402 fclose (fp);
403 return 1;
406 static void
407 make_device_map (const char *device_map, int floppy_disks)
409 FILE *fp;
410 int num_hd = 0;
411 int i;
413 if (strcmp (device_map, "-") == 0)
414 fp = stdout;
415 else
416 fp = fopen (device_map, "w");
418 if (! fp)
419 grub_util_error ("cannot open %s", device_map);
421 /* Floppies. */
422 for (i = 0; i < floppy_disks; i++)
424 char name[16];
425 struct stat st;
427 get_floppy_disk_name (name, i);
428 if (stat (name, &st) < 0)
429 break;
430 /* In floppies, write the map, whether check_device succeeds
431 or not, because the user just may not insert floppies. */
432 if (fp)
433 fprintf (fp, "(fd%d)\t%s\n", i, name);
436 #ifdef __linux__
437 if (have_devfs ())
439 while (1)
441 char discn[32];
442 char name[PATH_MAX];
443 struct stat st;
445 /* Linux creates symlinks "/dev/discs/discN" for convenience.
446 The way to number disks is the same as GRUB's. */
447 sprintf (discn, "/dev/discs/disc%d", num_hd);
448 if (stat (discn, &st) < 0)
449 break;
451 if (realpath (discn, name))
453 char *p;
454 strcat (name, "/disc");
455 p = grub_util_get_disk_name (num_hd, name);
456 fprintf (fp, "(%s)\t%s\n", p, name);
457 free (p);
460 num_hd++;
463 goto finish;
465 #endif /* __linux__ */
467 /* IDE disks. */
468 for (i = 0; i < 20; i++)
470 char name[16];
472 get_ide_disk_name (name, i);
473 if (check_device (name))
475 char *p;
476 p = grub_util_get_disk_name (num_hd, name);
477 fprintf (fp, "(%s)\t%s\n", p, name);
478 free (p);
479 num_hd++;
483 #ifdef __linux__
484 /* Virtio disks. */
485 for (i = 0; i < 20; i++)
487 char name[16];
489 get_virtio_disk_name (name, i);
490 if (check_device (name))
492 char *p;
493 p = grub_util_get_disk_name (num_hd, name);
494 fprintf (fp, "(%s)\t%s\n", p, name);
495 free (p);
496 num_hd++;
500 /* ATARAID disks. */
501 for (i = 0; i < 8; i++)
503 char name[20];
505 get_ataraid_disk_name (name, i);
506 if (check_device (name))
508 char *p;
509 p = grub_util_get_disk_name (num_hd, name);
510 fprintf (fp, "(%s)\t%s\n", p, name);
511 free (p);
512 num_hd++;
516 /* Xen virtual block devices. */
517 for (i = 0; i < 16; i++)
519 char name[16];
521 get_xvd_disk_name (name, i);
522 if (check_device (name))
524 char *p;
525 p = grub_util_get_disk_name (num_hd, name);
526 fprintf (fp, "(%s)\t%s\n", p, name);
527 free (p);
528 num_hd++;
531 #endif /* __linux__ */
533 /* The rest is SCSI disks. */
534 for (i = 0; i < 16; i++)
536 char name[16];
538 get_scsi_disk_name (name, i);
539 if (check_device (name))
541 char *p;
542 p = grub_util_get_disk_name (num_hd, name);
543 fprintf (fp, "(%s)\t%s\n", p, name);
544 free (p);
545 num_hd++;
549 #ifdef __linux__
550 /* This is for DAC960 - we have
551 /dev/rd/c<controller>d<logical drive>p<partition>.
553 DAC960 driver currently supports up to 8 controllers, 32 logical
554 drives, and 7 partitions. */
556 int controller, drive;
558 for (controller = 0; controller < 8; controller++)
560 for (drive = 0; drive < 15; drive++)
562 char name[24];
564 get_dac960_disk_name (name, controller, drive);
565 if (check_device (name))
567 char *p;
568 p = grub_util_get_disk_name (num_hd, name);
569 fprintf (fp, "(%s)\t%s\n", p, name);
570 free (p);
571 num_hd++;
577 /* This is for CCISS - we have
578 /dev/cciss/c<controller>d<logical drive>p<partition>. */
580 int controller, drive;
582 for (controller = 0; controller < 3; controller++)
584 for (drive = 0; drive < 10; drive++)
586 char name[24];
588 get_cciss_disk_name (name, controller, drive);
589 if (check_device (name))
591 char *p;
592 p = grub_util_get_disk_name (num_hd, name);
593 fprintf (fp, "(%s)\t%s\n", p, name);
594 free (p);
595 num_hd++;
601 /* This is for Compaq Intelligent Drive Array - we have
602 /dev/ida/c<controller>d<logical drive>p<partition>. */
604 int controller, drive;
606 for (controller = 0; controller < 3; controller++)
608 for (drive = 0; drive < 10; drive++)
610 char name[24];
612 get_ida_disk_name (name, controller, drive);
613 if (check_device (name))
615 char *p;
616 p = grub_util_get_disk_name (num_hd, name);
617 fprintf (fp, "(%s)\t%s\n", p, name);
618 free (p);
619 num_hd++;
625 /* This is for I2O - we have /dev/i2o/hd<logical drive><partition> */
627 char unit;
629 for (unit = 'a'; unit < 'f'; unit++)
631 char name[24];
633 get_i2o_disk_name (name, unit);
634 if (check_device (name))
636 char *p;
637 p = grub_util_get_disk_name (num_hd, name);
638 fprintf (fp, "(%s)\t%s\n", p, name);
639 free (p);
640 num_hd++;
645 /* MultiMediaCard (MMC). */
646 for (i = 0; i < 10; i++)
648 char name[16];
650 get_mmc_disk_name (name, i);
651 if (check_device (name))
653 char *p;
654 p = grub_util_get_disk_name (num_hd, name);
655 fprintf (fp, "(%s)\t%s\n", p, name);
656 free (p);
657 num_hd++;
661 finish:
662 #endif /* __linux__ */
664 if (fp != stdout)
665 fclose (fp);
668 static struct option options[] =
670 {"device-map", required_argument, 0, 'm'},
671 {"probe-second-floppy", no_argument, 0, 's'},
672 {"no-floppy", no_argument, 0, 'n'},
673 {"help", no_argument, 0, 'h'},
674 {"version", no_argument, 0, 'V'},
675 {"verbose", no_argument, 0, 'v'},
676 {0, 0, 0, 0}
679 static void
680 usage (int status)
682 if (status)
683 fprintf (stderr,
684 "Try ``grub-mkdevicemap --help'' for more information.\n");
685 else
686 printf ("\
687 Usage: grub-mkdevicemap [OPTION]...\n\
689 Generate a device map file automatically.\n\
691 -n, --no-floppy do not probe any floppy drive\n\
692 -s, --probe-second-floppy probe the second floppy drive\n\
693 -m, --device-map=FILE use FILE as the device map [default=%s]\n\
694 -h, --help display this message and exit\n\
695 -V, --version print version information and exit\n\
696 -v, --verbose print verbose messages\n\
698 Report bugs to <%s>.\n\
700 DEFAULT_DEVICE_MAP, PACKAGE_BUGREPORT);
702 exit (status);
706 main (int argc, char *argv[])
708 char *dev_map = 0;
709 int floppy_disks = 1;
711 progname = "grub-mkdevicemap";
713 /* Check for options. */
714 while (1)
716 int c = getopt_long (argc, argv, "snm:r:hVv", options, 0);
718 if (c == -1)
719 break;
720 else
721 switch (c)
723 case 'm':
724 if (dev_map)
725 free (dev_map);
727 dev_map = xstrdup (optarg);
728 break;
730 case 'n':
731 floppy_disks = 0;
732 break;
734 case 's':
735 floppy_disks = 2;
736 break;
738 case 'h':
739 usage (0);
740 break;
742 case 'V':
743 printf ("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
744 return 0;
746 case 'v':
747 verbosity++;
748 break;
750 default:
751 usage (1);
752 break;
756 make_device_map (dev_map ? : DEFAULT_DEVICE_MAP, floppy_disks);
758 free (dev_map);
760 return 0;