home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / hpfslin9.zip / setboot / set-boot.c < prev    next >
C/C++ Source or Header  |  1993-10-23  |  10KB  |  435 lines

  1. #include <assert.h>
  2. #include <stdio.h>
  3. #include <stdarg.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <fcntl.h>
  7. #include <string.h>
  8. #include <getopt.h>
  9.  
  10. #include "boot-manager.h"
  11.  
  12. /* superset of boot manager menu, one entry per partition.
  13.    The boot manager menu entries are the ones marked bootable.
  14.    'Startable' is boot-manager-ese for active, it is only interesting
  15.    for primary partitions on the first disk. */
  16.  
  17. struct partition_info
  18. {
  19.   char tag[8];                /* name used in menu */
  20.   char dev[8];                /* /dev/hdxy or /dev/sdxy */
  21.   unsigned secno;            /* first sector number */
  22.   unsigned char bootable, startable;    /* boot mgr flags */
  23.   unsigned char devno;            /* devno 0 = 80h, 1 = 81h, ... */
  24.   unsigned char partno;            /* partno 0-3 primary, 5 up logical */
  25. };
  26.  
  27. struct partition_info info[256];
  28. unsigned n_partitions;
  29.  
  30. /* file descriptor and device name for disk or partition we're examining */
  31.  
  32. int fd;
  33. char dev[16];
  34.  
  35. /* a global to number the disks, first the /dev/hdx's then the /dev/sdx's */   
  36.  
  37. unsigned devno;
  38.  
  39. /* forwards */
  40.  
  41. void read_partition_info (void);
  42. int read_partitions (void);
  43. void read_boot_manager (void);
  44. void list_partitions (void);
  45. void setboot (const char *tag);
  46. void setboot_1 (const char *tag, char drive);
  47. void list_menu (void);
  48. void *get1 (unsigned secno);
  49. void put1 (void *buf, unsigned secno);
  50. void fatal_perror (const char *fmt, ...);
  51. void no_boot_manager (void);
  52. void usage (void);
  53.  
  54. /* main. */
  55.  
  56. int main (int argc, char *argv[])
  57. {
  58.   char *bootflag;
  59.   int c;
  60.   int lflag;
  61.  
  62.   lflag = 0;
  63.   bootflag = 0;
  64.  
  65.   while ((c = getopt (argc, argv, "l")) != -1)
  66.     switch (c) {
  67.     case 'l':                /* -l => list partitions, for debug */
  68.       lflag = 1;
  69.       break;
  70.     case '?':
  71.       usage ();
  72.     }
  73.       
  74.   /* arg, if present, is what to boot */
  75.   if (optind < argc)        
  76.     bootflag = argv[optind++];
  77.  
  78.   /* check no extra args */
  79.   if (optind != argc)        
  80.     usage ();
  81.  
  82.   /* read partition tables */
  83.   read_partition_info ();
  84.  
  85.   /* dump them if wanted */
  86.   if (lflag)
  87.     list_partitions ();
  88.  
  89.   /* do it */
  90.   setboot (bootflag);
  91.  
  92.   return 0;
  93. }
  94.  
  95. /* Fetch partition tables into partition_info */
  96.  
  97. void read_partition_info ()
  98. {
  99.   int n;
  100.  
  101.   for (n = 0; ; n++) {
  102.     sprintf (dev, "/dev/hd%c", n + 'a');
  103.     if (! read_partitions ())
  104.       break;
  105.   }
  106.  
  107.   for (n = 0; ; n++) {
  108.     sprintf (dev, "/dev/sd%c", n + 'a');
  109.     if (! read_partitions ())
  110.       break;
  111.   }
  112.  
  113.   read_boot_manager ();
  114. }
  115.  
  116. /* Fetch partition tables from one disk.  Global 'dev' is its name. */
  117.  
  118. int read_partitions ()
  119. {
  120.   struct partition_boot_sector *p;
  121.   unsigned extended_start, logical_secno, logical_partno;
  122.   int n;
  123.  
  124.   assert (sizeof *p == 512);
  125.  
  126.   /* open device /dev/hdx (whole drive) for reading */
  127.   fd = open (dev, 0);
  128.   if (fd < 0)
  129.     return 0;
  130.  
  131.   /* get master boot record, sector 0, with partition table */
  132.   p = get1 (0);
  133.   extended_start = 0;
  134.  
  135.   /* Stash info.  Partition type 5 heads a linked list of logical partitions */
  136.  
  137.   for (n = 0; n < 4; n++)
  138.     if (p->partition[n].sector_number) {
  139.       info[n_partitions].devno = devno;
  140.       info[n_partitions].partno = n;
  141.       info[n_partitions].secno = p->partition[n].sector_number;
  142.       info[n_partitions].startable = p->partition[n].active;
  143.       sprintf (info[n_partitions].dev, "%s%d", dev+5, n+1);
  144.       if (p->partition[n].system_indicator == 5)
  145.     extended_start = p->partition[n].sector_number;
  146.       n_partitions++;
  147.     }
  148.  
  149.   /* If we saw a type-5 partition, its starting sector number points to
  150.      the first logical partition.  The first sector of that partition is its
  151.      partition boot record, which points to the next logical partition.
  152.      All sector numbers in logical partition boot records are relative to
  153.      the start of the extended partition, the type-5 entry in the primary
  154.      partition table. */
  155.  
  156.   if (extended_start) {
  157.  
  158.     /* walk down the linked list, fetching the boot manager menu tag
  159.        from each logical partition. */
  160.  
  161.     logical_secno = 0;
  162.  
  163.     for (logical_partno = 5; ; logical_partno++) {
  164.       unsigned t = extended_start + logical_secno; 
  165.       p = get1 (t);
  166.  
  167.       logical_secno = 0;
  168.       for (n = 0; n < 4; n++)
  169.     if (p->partition[n].sector_number)
  170.       if (p->partition[n].system_indicator == 5)
  171.         logical_secno = p->partition[n].sector_number;
  172.       else {
  173.         info[n_partitions].devno = devno;
  174.         info[n_partitions].partno = logical_partno;
  175.         info[n_partitions].secno = t;
  176.         info[n_partitions].startable = p->partition[n].active;
  177.         info[n_partitions].bootable = p->bootable;
  178.         memcpy (info[n_partitions].tag, p->tag, sizeof p->tag);
  179.         sprintf (info[n_partitions].dev, "%s%d", dev+5, logical_partno);
  180.         n_partitions++;
  181.       }
  182.  
  183.       if (! logical_secno)
  184.     break;
  185.     }
  186.   }
  187.  
  188.   /* done with this disk, increment devno for next disk */
  189.  
  190.   devno++;
  191.   close (fd);
  192.   return 1;
  193. }
  194.  
  195. /* Fetch boot manager data from the boot manager partition.
  196.    To find boot manager, look in the partition that will get booted -- active
  197.    partition on first disk.  */
  198.  
  199. void read_boot_manager ()
  200. {
  201.   int n;
  202.   struct bootmgr_boot_block *bootblock;
  203.   struct boot_manager_menu *menublock;
  204.  
  205.   /* Find active primary partition on first disk */
  206.  
  207.   for (n = 0; n < n_partitions; n++)
  208.     if (info[n].startable)
  209.       break;
  210.  
  211.   if (n == n_partitions || info[n].devno != 0 || info[n].partno > 3)
  212.     no_boot_manager ();
  213.  
  214.   /* Set 'dev' to, eg, /dev/sda3, the Linux name for the boot mgr partition.
  215.      'dev' will remain set to boot manager from now on */
  216.  
  217.   sprintf (dev, "/dev/%s", info[n].dev);
  218.   fd = open (dev, 0);
  219.   if (fd < 0)
  220.     no_boot_manager ();
  221.  
  222.   /* Fetch boot mgr's boot sector and see if it has magic oem-id */
  223.  
  224.   bootblock = get1 (0);
  225.   if (memcmp (bootblock->oem_id, "APJ&WN", 5))
  226.     no_boot_manager ();
  227.  
  228.   /* Sector 3 of boot manager has the menu info for primary partitions
  229.      on all disks */
  230.  
  231.   menublock = get1 (3);
  232.  
  233.   for (n = 0; n < n_partitions; n++) {
  234.     if (info[n].partno <= 3) {
  235.       struct partition_data *p = &menublock->partition_data[info[n].devno * 4
  236.                                 + info[n].partno];
  237.       info[n].bootable = p->bootable;
  238.       memcpy (info[n].tag, p->tag, sizeof p->tag);
  239.     }
  240.   }
  241.  
  242.   close (fd);
  243. }
  244.  
  245. /* Dump the assembled menu */
  246.  
  247. void list_partitions ()
  248. {
  249.   int n;
  250.  
  251.   for (n = 0; n < n_partitions; n++) {
  252.     struct partition_info *p = &info[n];
  253.     printf ("/dev/%-7s %8.8s %s %s %8x %d %d\n",
  254.         p->dev,
  255.         p->tag,
  256.         p->bootable ? "bootable" : "        ",
  257.         p->startable ? "startable" : "         ",
  258.         p->secno,
  259.         p->devno, p->partno);
  260.   }
  261. }
  262.  
  263. /* Compare the user's menu selection with the boot manager menu and see if
  264.    it's there.  All this hassle is because giving an invalid name causes
  265.    boot manager to emit the paralyzing message
  266.  
  267.        Operating system missing, system halted
  268.  
  269.    so do a validity check.  Since we're at it, allow unique abbrevs and
  270.    case insensitivity */
  271.  
  272. void setboot (const char *tag)
  273. {
  274.   int n;
  275.   const char *iba;
  276.   unsigned ibd;
  277.   int taglen = strlen(tag);
  278.  
  279.   /* Just 'setboot' lists the menu */
  280.  
  281.   if (! tag)
  282.     list_menu ();
  283.  
  284.   /* setboot X: stores that letter to be booted from.
  285.      I don't know how to validity-check this one */
  286.  
  287.   else if (tag[taglen-1] == ':') {
  288.     if (taglen != 2)
  289.       usage ();
  290.     ibd = tag[0];
  291.     if (ibd - 'a' < 26) ibd -= 040;
  292.     if (ibd - 'A' < 26)
  293.       setboot_1 (0, ibd);
  294.     else
  295.       usage ();
  296.   }
  297.  
  298.   /* setboot TAG selects that tag from the menu */
  299.   
  300.   else {
  301.     iba = 0;
  302.     for (n = 0; n < n_partitions; n++) {
  303.       struct partition_info *p = &info[n];
  304.       if (p->bootable && ! strncasecmp (tag, p->tag, taglen)) {
  305.     if (iba) {
  306.       fprintf (stderr, "Ambiguous.\n");
  307.       list_menu ();
  308.       return;
  309.     }
  310.     iba = p->tag;
  311.       }
  312.     }
  313.  
  314.     /* if no match, give help, otherwise store the full name for boot mgr */
  315.     if (! iba)
  316.       list_menu ();
  317.     else
  318.       setboot_1 (iba, 0);
  319.   }
  320. }
  321.  
  322. /* Actually write the chosen tag or drive letter into sector 1 of boot
  323.    manager's partition.  */
  324.  
  325. void setboot_1 (const char *tag, char drive)
  326. {
  327.   struct boot_manager_transient *p;
  328.  
  329.   /* Open /dev/bootmgr for writing */
  330.  
  331.   fd = open (dev, 2);
  332.   if (fd < 0)
  333.     fatal_perror ("can't write %s", dev);
  334.  
  335.   /* get sector 1 */
  336.  
  337.   p = get1 (1);
  338.  
  339.   /* insert /IBD:X or /IBA:TAG into its place, and clear the other one */
  340.  
  341.   if (tag) {
  342.     p->boot_device = 0;
  343.     memcpy (p->boot_tag, tag, 8);
  344.   } else {
  345.     p->boot_device = drive;
  346.     bzero (p->boot_tag, 8);
  347.   }
  348.     
  349.   /* write sector 1 back */
  350.  
  351.   put1 (p, 1);
  352.  
  353.   /* tell what we did */
  354.  
  355.   if (tag)
  356.     printf ("Next boot loads %.8s\n", tag);
  357.   else
  358.     printf ("Next boot from drive %c:\n", drive);
  359.  
  360.   close (fd);
  361. }
  362.  
  363. /* print the boot manager tags of the partitions marked Bootable */
  364.  
  365. void list_menu (void)
  366. {
  367.   int n, i;
  368.  
  369.   printf ("Bootable partitions: ");
  370.   for (n = 0; n < n_partitions; n++) {
  371.     struct partition_info *p = &info[n];
  372.     if (p->bootable) {
  373.       for (i = 8; i > 0; i--)
  374.     if (p->tag[i-1] != ' ') break;
  375.       printf (" %*.*s", i, i, p->tag);
  376.     }
  377.   }
  378.   printf ("\n");
  379. }
  380.  
  381. /* read a sector */
  382.  
  383. void *get1 (unsigned secno)
  384. {
  385.   char *buf = malloc (512);
  386.   
  387.   if (lseek (fd, secno * 512, 0) < 0)
  388.     fatal_perror ("lseek %s to sector %d", dev, secno);
  389.   if (read (fd, buf, 512) != 512)
  390.     fatal_perror ("read %s", dev);
  391.  
  392.   return buf;
  393. }
  394.  
  395. /* write a sector */
  396.  
  397. void put1 (void *buf, unsigned secno)
  398. {
  399.   if (lseek (fd, secno * 512, 0) < 0)
  400.     fatal_perror ("lseek %s to sector %d", dev, secno);
  401.   if (write (fd, buf, 512) != 512)
  402.     fatal_perror ("write %s", dev);
  403. }
  404.  
  405. /* fall over dead */
  406.  
  407. void fatal_perror (const char *fmt, ...)
  408. {
  409.   va_list ap;
  410.   char buf[128];
  411.   va_start (ap, fmt);
  412.   vsprintf (buf, fmt, ap);
  413.   va_end (ap);
  414.   perror (buf);
  415.   exit (1);
  416. }
  417.  
  418. /* explain regretfully */
  419.  
  420. void no_boot_manager ()
  421. {
  422.   fprintf (stderr, "Can't find boot manager\n");
  423.   exit (1);
  424. }
  425.  
  426. /* print the documentation, in full */
  427.  
  428. void usage ()
  429. {
  430.   fprintf (stderr, "Usage: setboot os-name    to boot the OS with that tag\n");
  431.   fprintf (stderr, "       setboot X:         to boot from OS/2 device letter X\n");
  432.   fprintf (stderr, "Note: does not run reboot or shutdown\n"); 
  433.   exit (1);
  434. }
  435.