home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / share / YaST2 / include / bootloader / routines / lilolike.ycp < prev    next >
Text File  |  2006-11-29  |  37KB  |  1,234 lines

  1. /**
  2.  * File:
  3.  *      include/bootloader/routines/lilolike.ycp
  4.  *
  5.  * Module:
  6.  *      Bootloader installation and configuration
  7.  *
  8.  * Summary:
  9.  *      Functions common for lilo-like bootloaders only
  10.  *
  11.  * Authors:
  12.  *      Jiri Srain <jsrain@suse.cz>
  13.  *
  14.  * $Id: lilolike.ycp 34590 2006-11-24 18:33:17Z aosthof $
  15.  *
  16.  */
  17. {
  18.  
  19. textdomain "bootloader";
  20.  
  21. import "Arch";
  22. import "Mode";
  23. import "Storage";
  24. import "StorageDevices";
  25. import "BootArch";
  26.  
  27. global string DiskOrderSummary ();
  28. global list<string> DisksOrder ();
  29. global define void DetectDisks ();
  30. global define boolean RefreshDisks();
  31. global void ProposeDeviceMap ();
  32.  
  33.  
  34. include "bootloader/routines/i386.ycp";
  35.  
  36. /**
  37.  * Is embedding 1.5 stage of bootloader to dedicated partition area possible?
  38.  * @return true if it is possible
  39.  */
  40. global boolean allowEmbed15 () {
  41.     // allow only for /boot or /root device selected
  42.     if (! (BootCommon::loader_device == BootCommon::BootPartitionDevice
  43.     || BootCommon::loader_device == BootCommon::RootPartitionDevice
  44.     || BootCommon::loader_device == BootCommon::mbrDisk
  45.     ))
  46.     {
  47.     return false;
  48.     }
  49.     // check filesystem on /boot for Reiserfs and JFS
  50.     map mp = Storage::GetMountPoints ();
  51.     list bp_info
  52.     = mp["/boot"]:mp["/"]:[];
  53.     list<map> partitions
  54.     = Storage::GetTargetMap ()[bp_info[2]:"", "partitions"]:[];
  55.     boolean ret = false;
  56.     foreach (map p, partitions, {
  57.     if (p["device"]:"" == BootCommon::BootPartitionDevice)
  58.     {
  59.         symbol fs = (symbol)p["used_fs"]:nil;
  60.         if (fs == `reiser || fs == `jfs)
  61.         ret = true;
  62.     }
  63.     });
  64.     return ret;
  65. }
  66. /**
  67.  * Check whether disk settings were changed since last checking
  68.  * @return boolean true if needs to recheck
  69.  */
  70. global boolean DisksChanged () {
  71.     if (Mode::config ())
  72.     return false;
  73.     map mp = Storage::GetMountPoints();
  74.     string actual_root = mp["/", 0]:"";
  75.     string actual_boot = mp["/boot", 0]:actual_root;
  76.  
  77.     // don't change configuration if '/' and '/boot' were not changed
  78.     // and location is "floppy", "mbr" or "boot"
  79.     if (actual_boot == BootCommon::BootPartitionDevice
  80.     && actual_root == BootCommon::RootPartitionDevice
  81.             && selected_location != "custom"
  82.             && selected_location != ""
  83.         && selected_location != nil)
  84.     {
  85.     return false;
  86.     }
  87.  
  88.     list all_partitions = BootCommon::getPartitionList(`boot);
  89.  
  90.     if (!contains(all_partitions, BootCommon::loader_device))
  91.     {
  92.     y2milestone ("Location should be set again");
  93.     return true;
  94.     }
  95.     return false;
  96. }
  97.  
  98. /**
  99.  * FindMbrDisk()
  100.  * try to find the system's mbr device
  101.  * @return string   mbr device
  102.  */
  103. global string FindMBRDisk() {
  104.     // check the disks order, first has MBR
  105.     list<string> order = DisksOrder ();
  106.     if (size (order) > 0)
  107.     {
  108.     string ret = order[0]:"";
  109.     y2milestone ("First disk in the order: %1, using for MBR", ret);
  110.     return ret;
  111.     }
  112.  
  113.     // OK, order empty, use the disk with boot partition
  114.     map mp = Storage::GetMountPoints();
  115.     string boot_disk = mp["/boot",2]:(mp["/",2]:"");
  116.     y2milestone ("Disk with boot partition: %1, using for MBR", boot_disk);
  117.     return boot_disk;
  118. }
  119.  
  120.     /**
  121.      * ConfigureLocation()
  122.      * Where to install the bootloader.
  123.      * Returns the type of device where to install: one of "boot", "root", "mbr", "mbr_md"
  124.      * Also sets internal global variable selected_location to this.
  125.      *
  126.      * Sets internal global variables:
  127.      *        - selected_location    to the type of bootloader device (currently one of: "boot", "root", "mbr", "mbr_md")
  128.      *        - loader_device    to the actual device name to install the bootloader to (e.g. "/dev/hda1") or to "mbr_md"
  129.      *        - activate        to true if the loader_device needs to be activated in the MBR
  130.      *      - activate_changed    leave untouched, except when
  131.      *                    - booting from a primary /boot partition on the first disk (the one with the MBR seen by the BIOS), then set to true
  132.      *                      (FIXME: why only then?)
  133.      *        - repl_mbr        leave untouched, except when
  134.      *                    - booting from a primary /boot partition on the first disk, then set to true when
  135.      *                    - the examination of the code in the MBR
  136.      *                        - by examine_mbr.pl shows that it
  137.      *                        - DOES     look like a LILO or GRUB MBR (always replace them with new versions)                AND
  138.      *                        - DOES NOT look like a "generic MBR" (= DOS MBR) (OK as stage 1 to boot primary part. on 1st disk)  AND
  139.      *                        - DOES NOT look like a valid "stage 1" at all (not enough entropy to contain valid code)        AND
  140.      *                        - DOES     look like some "stage 1" code (has enough entropy, but no known signature)            AND
  141.      *                        - by KeepMBR() -> ThinkPadMBR() shows that it
  142.      *                        - DOES NOT look like a Thinkpad MBR (begins with specific code sequence from that one)
  143.      *                      otherwise set to false
  144.      *
  145.      * @return string type of location proposed to bootloader
  146.      */
  147.     global define string ConfigureLocation() ``{
  148.         selected_location = "mbr";             // default to mbr
  149.         loader_device   = BootCommon::mbrDisk;
  150.     // check whether the /boot partition
  151.     //  - is primary:                is_logical    -> false
  152.     //  - is on the first disk (with the MBR):  disk_is_mbr -> true
  153.     map<string,any> tm = Storage::GetTargetMap ();
  154.     map dp = Storage::GetDiskPartition (BootPartitionDevice);
  155.     string disk = dp["disk"]:"";
  156.     boolean disk_is_mbr = disk == mbrDisk;
  157.     map dm = tm[disk]:$[];
  158.     list<map> partitions = dm["partitions"]:[];
  159.     boolean is_logical = false;
  160.     string extended = nil;
  161.     list<string> needed_devices = [ BootPartitionDevice ];
  162.     map<string,integer> md_info = Md2Partitions (BootPartitionDevice);
  163.     if (md_info != nil && size (md_info) > 0)
  164.     {
  165.         disk_is_mbr = false;
  166.         needed_devices = maplist (string d, integer b, md_info, {
  167.         map pdp = Storage::GetDiskPartition (d);
  168.         string p_disk = pdp["disk"]:"";
  169.         if (p_disk == mbrDisk)
  170.             disk_is_mbr = true;
  171.         return d;
  172.         });
  173.     }
  174.     y2milestone ("Boot partition devices: %1", needed_devices);
  175.     foreach (map p, partitions, {
  176.         if (p["type"]:nil == `extended)
  177.         {
  178.         extended = (string)p["device"]:nil;
  179.         }
  180.         else if (contains (needed_devices, p["device"]:"")
  181.             && p["type"]:nil == `logical)
  182.         {
  183.         is_logical = true;
  184.         }
  185.     });
  186.     y2milestone ("/boot is on 1st disk: %1", disk_is_mbr);
  187.     y2milestone ("/boot is in logical partition: %1", is_logical);
  188.     y2milestone ("The extended partition: %1", extended);
  189.  
  190.     // keep_mbr, if the MBR contains special code that needs to be kept,
  191.     //           like Thinkpad boot code (and ATM only Thinkpad boot code
  192.     //           is recognized)
  193.     boolean keep_mbr = KeepMBR (loader_device);
  194.  
  195.     // if is primary, store bootloader there
  196.     if (disk_is_mbr && ! is_logical)
  197.     {
  198.         selected_location = "boot";
  199.         loader_device = BootPartitionDevice;
  200.         activate = true;
  201.         activate_changed = true;
  202.         // examine_mbr.pl returns
  203.         //        - 0 for a "Generic MBR" (DOS MBR)
  204.         //        - 0 for an unknown MBR
  205.         //        - 1 for a GRUB or lilo "stage 1"
  206.         //        - 1 for an "invalid MBR", i.e. without enough entropy to
  207.         //        contain boot code
  208.         map out = (map)SCR::Execute (.target.bash_output, sformat (
  209.         "/usr/lib/YaST2/bin/examine_mbr.pl %1", disk));
  210.         y2milestone ("MBR examining script returned %1", out);
  211.         integer exit = out["exit"]:0;
  212.         repl_mbr = (exit != 0) && (! keep_mbr);
  213.     }
  214.     else if (size (needed_devices) > 1)
  215.     {
  216.         loader_device = "mbr_md";
  217.         selected_location = "mbr_md";
  218.     }
  219.  
  220.     if (keep_mbr)
  221.     {
  222.         if (is_logical && extended != nil)
  223.         loader_device = extended;
  224.         else
  225.         loader_device = BootPartitionDevice;
  226.         selected_location = "boot";
  227.     }
  228.     if (! contains (getPartitionList (`boot), loader_device))
  229.     {
  230.         selected_location = "mbr";             // default to mbr
  231.         loader_device   = BootCommon::mbrDisk;
  232.     }
  233.  
  234.         y2milestone ("ConfigureLocation (%1 on %2)",
  235.         selected_location, loader_device);
  236.  
  237.     // set active flag
  238.     if (selected_location == "mbr")
  239.     {
  240.         // we are installing into MBR:
  241.         // if there is an active partition, then we do not need to activate
  242.         // one (otherwise we do)
  243.         activate = size (Storage::GetBootPartition (mbrDisk)) == 0;
  244.     }
  245.     else
  246.     {
  247.         // if not installing to MBR, always activate
  248.         activate = true;
  249.     }
  250.  
  251.         return selected_location;
  252.     }
  253.  
  254.     /**
  255.       * Detect /boot and / (root) partition devices
  256.       * If loader_device is empty or the device is not available as a boot
  257.       * partition, also calls ConfigureLocation to configure loader_device, set
  258.       * selected_location and set the activate flag if needed
  259.       * all these settings are stored in internal variables
  260.       */
  261.     global define void DetectDisks () ``{
  262. /*    map tm = Storage::GetTargetMap ();
  263.     list partitions = [];
  264.  
  265.     foreach ( string dev, map disk, tm, ``{
  266.         if( Storage::IsRealDisk( disk ) )
  267.         {
  268.         list l = filter( map p, disk["partitions"]:[],
  269.             ``(p["delete"]:false==false) );
  270.         partitions = merge (partitions, l);
  271.             }
  272.         });*/
  273.  
  274.     // #151501: AutoYaST needs to know the activate flag and the
  275.     // loader_device; jsrain also said this code is probably a bug:
  276.     // commenting out, but this may need to be changed and made dependent
  277.     // on a "clone" flag (i.e. make the choice to provide minimal (i.e. let
  278.     // YaST do partial proposals on the target system) or maximal (i.e.
  279.     // stay as closely as possible to this system) info in the AutoYaST XML
  280.     // file)
  281.     // if (Mode::config ())
  282.     //    return;
  283.         map mp = Storage::GetMountPoints();
  284.  
  285.         list mountdata_boot = mp["/boot"]:(mp["/"]:[]);
  286.     list mountdata_root = mp["/"]:[];
  287.  
  288.         y2milestone( "mountPoints %1", mp );
  289.         y2milestone( "mountdata_boot %1", mountdata_boot );
  290.  
  291.         BootCommon::RootPartitionDevice = mp["/", 0]:"";
  292.  
  293.         if (BootCommon::RootPartitionDevice == "")
  294.         {
  295.             y2error ("No mountpoint for / !!");
  296.         }
  297.  
  298.         // if /boot changed, re-configure location
  299.         BootCommon::BootPartitionDevice
  300.             = mountdata_boot[0]:BootCommon::RootPartitionDevice;
  301.  
  302.     if (BootCommon::mbrDisk == "" || BootCommon::mbrDisk == nil)
  303.     {
  304.         // mbr detection.
  305.         BootCommon::mbrDisk = FindMBRDisk();
  306.     }
  307.  
  308.     if (loader_device == nil || loader_device == ""
  309.         || ! contains (getPartitionList (`boot), loader_device))
  310.             ConfigureLocation ();
  311.  
  312. /*    if (mountdata_boot[3]:"" == "raid1")
  313.     {
  314.         list md_list = filter (`e, partitions,
  315.         ``(e["used_by"]:"" == substring(mountdata_boot[0]:"",5)));
  316.         list dev_list = maplist (`e, md_list, ``(e["device"]:""));
  317.         dev_list = filter (`d, dev_list, ``(d != ""));
  318.         if (size (dev_list) > 0)
  319.         {
  320.         dev_list = sort (dev_list);
  321.         BootCommon::BootPartitionDevice = dev_list[0]:"";
  322.         }
  323.     }
  324.         if (mountdata_root[3]:"" == "raid1")
  325.         {
  326.             list md_list = filter (`e, partitions,
  327.         ``(e["used_by"]:"" == substring(mountdata_root[0]:"",5)));
  328.             list dev_list = maplist (`e, md_list, ``(e["device"]:""));
  329.             dev_list = filter (`d, dev_list, ``(d != ""));
  330.             if (size (dev_list) > 0)
  331.             {
  332.                 dev_list = sort (dev_list);
  333.                 BootCommon::RootPartitionDevice = dev_list[0]:"";
  334.             }
  335.         }*/
  336.     }
  337.  
  338.     /**
  339.       * Converts the md device to the list of devices building it
  340.       * @param md_device string md device
  341.       * @return a map of devices (from device name to BIOS ID or nil if
  342.       *   not detected) building the md device
  343.       */
  344.     global define map<string, integer> Md2Partitions (string md_device) {
  345.     map<string,integer> ret = $[];
  346.     map<string,any> tm = (map<string,map>)Storage::GetTargetMap();
  347.     foreach (string disk, any descr_a, tm, ``{
  348.         map<string,any> descr = (map<string,any>)descr_a;
  349.         string bios_id_str = descr["bios_id"]:"";
  350.         integer bios_id = 128; // maximum + 1
  351.         if (bios_id_str != "")
  352.         bios_id = tointeger (bios_id);
  353.         list<map<string,any> > partitions = (list<map<string,any> >)
  354.         descr["partitions"]:[];
  355.         foreach (map<string,any> partition, partitions, ``{
  356.         if (partition["used_by"]:"" == substring(md_device,5))
  357.         {
  358.             string d = (string)(partition["device"]:"");
  359.             ret[d] = bios_id;
  360.         }
  361.         });
  362.     });
  363.     y2milestone ("Partitions building %1: %2", md_device, ret);
  364.     return ret;
  365.     }
  366.  
  367.     /**
  368.       * Converts the md device to the first of its members
  369.       * @param md_device string md device
  370.       * @return string one of devices building the md array
  371.       */
  372.     global define string Md2Partition (string md_device) {
  373.     map<string,integer> devices = Md2Partitions (md_device);
  374.     if (size (devices) == 0)
  375.         return md_device;
  376.     integer minimal = 129; // maximum + 2
  377.     string found = "";
  378.     foreach (string k, integer v, devices, {
  379.         if (v < minimal)
  380.         {
  381.         found = k;
  382.         minimal = v;
  383.         }
  384.     });
  385.     return found;
  386. //    devices = (list<string>)sort (devices);
  387. //    return devices[0]:"";
  388.     }
  389.  
  390.     /**
  391.       * Get the md device a partition belongs to (or the partition itself if
  392.       * it doesn't exist
  393.       * @param device string a partition
  394.       * @return string the md device
  395.       */
  396.     global define string Partition2Md (string device) {
  397.     string ret = device;
  398.     map<string,any> tm = (map<string,map>)Storage::GetTargetMap();
  399.     foreach (string disk, any descr_a, tm, ``{
  400.         map<string,any> descr = (map<string,any>)descr_a;
  401.         list<map<string,any> > partitions = (list<map<string,any> >)
  402.         descr["partitions"]:[];
  403.         foreach (map<string,any> partition, partitions, ``{
  404.         if (partition["device"]:"" == device)
  405.         {
  406.             ret = (string)(partition["used_by"]:device);
  407.             if( search( ret, "/dev/" )!=0 )
  408.             ret = "/dev/"+ret;
  409.         }
  410.         });
  411.     });
  412.     y2milestone ("Partition %1 builds %2", device, ret);
  413.     return ret;
  414.     }
  415.  
  416.     /**
  417.       * Refresh disk locations
  418.       * @return boolean true if bootloader location should be set again
  419.       */
  420.     global define boolean RefreshDisks() ``{
  421.     boolean ret = true;
  422.     if (! DisksChanged ())
  423.         ret = false;
  424.  
  425.     y2milestone ("Reconfiguring locations");
  426.     DetectDisks ();
  427.  
  428.     return ret;
  429.     }
  430.  
  431. /**
  432.  * Answer whether LBA is supported
  433.  * @return boolean true if supported
  434.  */
  435. global boolean LbaSupport() {
  436.     if (Arch::i386 ())
  437.     {
  438.     list internal_bios = (list<map>)SCR::Read (.probe.bios);
  439.     return internal_bios[0, "lba_support"]:false;
  440.     }
  441.     else
  442.     return true;
  443. }
  444.  
  445.     /**
  446.      *  IsBootAccessible()
  447.      *  @return boolean true if accessible
  448.      */
  449.     global define boolean IsBootAccessible() ``{
  450.     if (Mode::config ())
  451.         return true;
  452.         boolean boot_partition_accessible = true;
  453.  
  454.         if (!LbaSupport())
  455.         {
  456.             string boot_mount_point = "";
  457.             // check, if no /boot partition exists
  458.  
  459.             if (Storage::GetMountPoints()["/boot"]:"" == "")
  460.             {
  461.                 boot_mount_point = "/";
  462.             }
  463.             else
  464.             {
  465.                 boot_mount_point = "/boot";
  466.             }
  467.  
  468.             // check if boot mount point is below cyl 1024
  469.  
  470.             foreach (string dname, map ddata, (map<string,map>)Storage::GetTargetMap(),
  471.             ``{
  472.                 list<map> partitions = ddata["partitions"]:[];
  473.                 if (partitions != [])
  474.                 {
  475.                     foreach (map pentry, partitions, ``{
  476.                         if (pentry["mount"]:"" == boot_mount_point)
  477.                         {
  478.                             boot_partition_accessible =
  479.                 (1024 > pentry["region",0]:0);
  480.                         }
  481.                     });
  482.                 }
  483.  
  484.             });
  485.  
  486.         }
  487.         if (boot_partition_accessible)
  488.             y2milestone("Boot partition accessible");
  489.         else
  490.             y2milestone("Boot partition unaccessible");
  491.  
  492.         return (boot_partition_accessible);
  493.     }
  494.  
  495.     /**
  496.       * Should backup copy of bootloader bootsector be created?
  497.       * @return boolean true if yes.
  498.       */
  499.     global define boolean createBackupBS () ``{
  500.     if (! Stage::initial ())
  501.         return false;
  502.     map mp = Storage::GetMountPoints ();
  503.     list data = mp["/boot"]:(mp["/"]:[]);
  504.     string bpd = data[0]:"";
  505.     // ???? FIXME ???? how about LVM/MD ????
  506.     return bpd == BootPartitionDevice;
  507.     }
  508.  
  509. /**
  510.  * Fix global section of lilo-like bootloader
  511.  * This currently only changes the "default" key to point to the first section,
  512.  * in case the referenced section does not exist anymore. An empty "default"
  513.  * value is not changed.
  514.  */
  515. global void FixGlobals () {
  516.     string defaultv = globals["default"]:"";
  517.     string first = "";
  518.     if (defaultv != "")
  519.     {
  520.     boolean exists = false;
  521.     foreach (map<string,any> s, sections, {
  522.         string label = s["name"]:"";
  523.         if (label == defaultv)
  524.         exists = true;
  525.         if (first == "")
  526.         first = label;
  527.     });
  528.     if (! exists)
  529.         globals["default"] = first;
  530.     }
  531. }
  532.  
  533. /**
  534.  * Fix section of lilo-like bootloader
  535.  */
  536. global void FixSections (void() create_sections) {
  537.     list<string> parts = getPartitionList(`parts_old);
  538.     if (partitioning_last_change
  539.         != Storage::GetTargetChangeTime()
  540.     && BootCommon::files_edited)
  541.     {
  542.     displayFilesEditedPopup ();
  543.     files_edited_warned = true;
  544.     return;
  545.     }
  546.  
  547.     // save old sections and propose new ones in global "sections"
  548.     // (the updated list of old sections will become the new section list in
  549.     // the end)
  550.     list<map<string,any> > old_sect_list = sections;
  551.  
  552.     create_sections ();
  553.  
  554.     // new_sect is a map with elements containing: "type" -> section
  555.     map<string,map<string,any> > new_sect = listmap (map<string,any> s,
  556.     sections,
  557.     {
  558.     string label = s["name"]:"";
  559.     string type = s["original_name"]:label;
  560.     return $[type: s];
  561.     });
  562.  
  563.     // remember a list with all the section "types" in the old section list
  564.     // (needed later in this function to find newly created sections)
  565.     list<string> old_section_types = maplist (map<string,any> s, old_sect_list,
  566.     {
  567.     return s["original_name"]:"";
  568.     });
  569.  
  570.     // in the old list of sections:
  571.     //    - only keep sections that the user created (no "__auto", or false) or
  572.     //      changed ("__changed") in the UI
  573.     //  - replace unchanged sections with ones we proposed just now (if
  574.     //    available)
  575.     //  - also notify user when devices for a "changed by user" section are
  576.     //    unavailable or would now be proposed differently (and mark section as
  577.     //    "created by user")
  578.     old_sect_list = maplist (map<string,any> s, old_sect_list, {
  579.     string label = s["name"]:"";
  580.     string type = s["original_name"]:label;
  581.     if (! s["__auto"]:false)
  582.     {
  583.         y2milestone ("Leaving section %1", label);
  584.         return s;
  585.     }
  586.     else if (! s["__changed"]:false)
  587.     {
  588.         y2milestone ("Recreating section %1, new is %2",
  589.         label, new_sect[type]:$[]);
  590.         return new_sect[type]:$[];
  591.     }
  592.     else
  593.     {
  594.         // section was created by us, then changed by the user:
  595.         //    - keep it
  596.         //  - maybe notify user to check it (and then mark it as a "user
  597.         //    defined section")
  598.         y2milestone ("Warning on section %1", label);
  599.         boolean cont = true;
  600.         // if "non-standard" section name and a used device is not
  601.         // available anymore, notify user
  602.         if (type != "linux" && type != "failsafe"
  603.         && type != "memtest86" && type != "wildcard")
  604.         {
  605.         foreach (string n, s["__devs"]:[], {
  606.             if (! contains (parts, n))
  607.             {
  608.             cont = false;
  609.             }
  610.         });
  611.         }
  612.         // if the devices for this section and the freshly created one of
  613.         // the same type are different, notify user
  614.         map<string,any> new_this_section = new_sect[type]:$[];
  615.         if (new_this_section == $[])
  616.         return $[];
  617.         list new_devs = toset(new_this_section["__devs"]:[]);
  618.         list old_devs = toset(s["__devs"]:[]);
  619.         if (size (new_devs) != size (old_devs))
  620.         cont = false;
  621.         else
  622.         {
  623.                 foreach (any d, old_devs, ``{
  624.             if (! contains (new_devs, d))
  625.             cont = false;
  626.                 });
  627.         }
  628.         // display popup for this section;
  629.         // also, next time we come through this function, consider this
  630.         // section as a section created by the user (and leave it as it is)
  631.         if (! cont)
  632.         {
  633.         s["__auto"] = false;
  634.         displayDiskChangePopup (label);
  635.         }
  636.         return s;
  637.     }
  638.     });
  639.  
  640.     // in newly created sections, fix "resume" parameter in append line if
  641.     // necessary
  642.     y2milestone ("Checking for sections using the resume parameter");
  643.     sections = maplist (map<string,any> s, BootCommon::sections, ``{
  644.     string append = s["append"]:"";
  645.     string resume = getKernelParamFromLine (append, "resume");
  646.     if (resume != "" && resume != nil
  647.         && ! haskey (getSwapPartitions (), resume))
  648.     // points to unexistent swap partition
  649.     {
  650.         append = setKernelParamToLine (append,
  651.         "resume", getLargestSwapPartition ());
  652.         s["append"] = append;
  653.     }
  654.     return s;
  655.     });
  656.  
  657.     // now add sections from newly created ones that were unknown before in the
  658.     // old section list, if not already removed by the user (#170469)
  659.     foreach (map<string,any> s, sections, {
  660.     string label = s["name"]:"";
  661.     string type = s["original_name"]:label;
  662.     if (! contains (old_section_types, type) &&
  663.         ! contains (removed_sections, type))
  664.     {
  665.         y2milestone ("Adding new section \"%1\": %2",
  666.         label, s);
  667.         old_sect_list = add(old_sect_list, s);
  668.         return s;
  669.     }
  670.     });
  671.  
  672.     // Strange (I must have misread the code here):
  673.     // if a newly created section uses one or more deleted devices, and a
  674.     // section of that type does not exist anymore in the old section list, add
  675.     // it to the old section list
  676.     y2milestone ("Checking for sections needing some of %1", del_parts);
  677.     list<string> to_remove = [];
  678.     foreach (map<string,any> s, sections, {
  679.     list<string> devs = s["__devs"]:[];
  680.     string label = s["name"]:"";
  681.     y2milestone ("Section %1 needs %2", label, devs);
  682.     boolean to_add = false;
  683.     foreach (string d, devs, {
  684.        if (contains (del_parts, d))
  685.         {
  686.         to_add = true;
  687.         }
  688.     });
  689.     if (to_add)
  690.     {
  691.         map old_sect = listmap (map<string,any> os, old_sect_list, {
  692.         return $[label: os];
  693.         });
  694.  
  695.         if (label != "" && ! haskey(old_sect, label))
  696.         {
  697.         y2milestone ("Adding %1", s);
  698.         to_remove = add (to_remove, label);
  699.         old_sect_list = add (old_sect_list, s);
  700.         }
  701.     }
  702.     });
  703.  
  704.     // FIXME: BUG: looks like a bug to remove a list of labels from the list of
  705.     // deleted devices
  706.     del_parts = (list<string>)difflist (del_parts, to_remove);
  707.  
  708.     // cleanup: throw away empty sections
  709.     old_sect_list = filter (map<string,any> s, old_sect_list, {
  710.     return s != $[];
  711.     });
  712.  
  713.     // save old, updated section list as proposed section list
  714.     sections = old_sect_list;
  715. }
  716.  
  717. /**
  718.  * Update sections of bootloader menu
  719.  * modifies internal structures
  720.  * @param replace boolean true if old sectinos shall be replaced
  721.  * @param create_linux_section a reference to a function to create linux
  722.  *   section anew
  723.  */
  724. global void UpdateSections (boolean replace,
  725.     map<string,any>(string) create_linux_section)
  726. {
  727.     list<map<string,any> > out = BootCommon::sections;
  728.     list<string> recreated = [];
  729.     boolean linux_resume_added = false;
  730.  
  731.     map<string,any> default_sect = create_linux_section ("linux");
  732.     string default_kernel = default_sect["kernel"]:"";
  733.     string default_initrd = default_sect["initrd"]:"";
  734.     string default_name = default_sect["name"]:"";
  735.  
  736.     list<string> sections_to_recreate = ["linux", "failsafe", "memtest86"];
  737.     if (getLoaderType (false) == "grub")
  738.     {
  739.     sections_to_recreate = add (sections_to_recreate, "xen");
  740.     }
  741.  
  742.     // if replace == true,  replace all sections that have a type in
  743.     //                      sections_to_recreate with a newly created version
  744.     // if replace == false, only adjust "append" line of "linux" section
  745.     //
  746.     // at the end of the loop, if one of the sections_to_recreate does not
  747.     // exist, create it
  748.     foreach (string t, sections_to_recreate, {
  749.     map<string,any> m = create_linux_section (t);
  750.     boolean f_changed = false;
  751.     out = maplist (map<string,any> s, out, {
  752.         string label = s["name"]:"";
  753.         string sect_type = s["original_name"]:"";
  754.         if (sect_type == "")
  755.         sect_type = label;
  756.         if (sect_type == t)
  757.         {
  758.         f_changed = true;
  759.         if (replace && m != $[])
  760.         {
  761.             recreated = add (recreated, label);
  762.             return m;
  763.         }
  764.         else if (t == "linux")
  765.         {
  766.             string append = s["append"]:"";
  767.             string resume = BootCommon::getKernelParamFromLine (
  768.             append, "resume");
  769.             if (! haskey (BootCommon::getSwapPartitions (), resume))
  770.             {
  771.             append = setKernelParamToLine (append,
  772.                 "resume", getLargestSwapPartition ());
  773.             s["append"] = append;
  774.             linux_resume_added = true;
  775.             }
  776.             return s;
  777.                 }
  778.         else
  779.             return s;
  780.         }
  781.         else
  782.         {
  783.         return s;
  784.         }
  785.     });
  786.     // if we did NOT change or replace the old section (meaning: there was
  787.     // none), but create_linux_section() gave us a new section, then
  788.     // prepend or append the section created by create_linux_section()
  789.         if (! f_changed && m != $[])
  790.     {
  791.         recreated = add (recreated, m["name"]:"");
  792.             if (t == "linux")
  793.         out = prepend (out, m);
  794.             else
  795.         out = add (out, m);
  796.     }
  797.     });
  798.  
  799.     // now adjust these keys in sections that need it:
  800.     //  - kernel
  801.     //  - initrd
  802.     //  - name
  803.     //  - device (e.g. for SATA: /dev/hda -> /dev/sda)
  804.     //  - append
  805.     out = maplist (map<string,any> s, out, {
  806.     string label = s["name"]:"";
  807.     string type = s["original_name"]:label;
  808.     foreach (string key, ["kernel", "initrd"], {
  809.         string value = s[key]:"";
  810.         if (regexpmatch (value, "^.*\.shipped.*$"))
  811.         {
  812.         value = regexpsub (value,
  813.             "^(.*)\.shipped(.*)$", "\\1\\2");
  814.         }
  815.         else if (regexpmatch (value, "^.*\.suse.*$"))
  816.         {
  817.         value = regexpsub (value,
  818.             "^(.*)\.suse(.*)$", "\\1\\2");
  819.         }
  820.         s["key"] = value;
  821.     });
  822.     // If we did not replace the sections anyway, adjust the section titles:
  823.     // Does this section
  824.     //  - use the default kernel of a linux section \ i.e. it uses the updated kernel
  825.     //  - use the default initrd of a linux section /
  826.     //  - contain the name of the first "linux" section read from disk in
  827.     //    its name
  828.     // then, update the section name
  829.     if (!replace
  830.         && s["kernel"]:"" == default_kernel
  831.         && s["initrd"]:"" == default_initrd
  832.         && issubstring (s["name"]:"", read_default_section_name)
  833.         && read_default_section_name != ""
  834.         && read_default_section_name != default_name)
  835.     {
  836.         // idea of this:
  837.         // orig_name == "linux":    "Linux"            -> "<new name>"
  838.         // orig_name == "failsafe": "Failsafe -- Linux" -> "Failsafe -- <new name>"
  839.         y2milestone ("Updating label of section %1...", s["name"]:"");
  840.         string old_name = s["name"]:"";
  841.         integer i1 = search (old_name, read_default_section_name);
  842.         integer i2 = i1 + size (read_default_section_name);
  843.         s["name"] = substring (old_name, 0, i1) + default_name
  844.         + substring (old_name, i2);
  845.         y2milestone ("... to %1", s["name"]:"");
  846.     }
  847.  
  848.     foreach (string key, ["root", "chainloader"], {
  849.         if ((contains (update_section_types, type)
  850.             && ! contains (recreated, label))
  851.         || key == "chainloader")
  852.         {
  853.         string device = s["key"]:"";
  854.         if (device != nil)
  855.         {
  856.             y2milestone ("Updating root/other device of section %1",
  857.             label);
  858.             device = BootCommon::UpdateDevice (device);
  859.             s["key"] = device;
  860.         }
  861.         }
  862.     });
  863.     if (type == "linux" && haskey (s, "append"))
  864.     {
  865.         string option = s["append"]:"";
  866.         foreach (string o, ListAdditionalKernelParams (), {
  867.         option = setKernelParamToLine (option, o, "false");
  868.         });
  869.         option = option + " " + GetAdditionalKernelParams ();
  870.         if (getKernelParamFromLine (option, "splash") == "false")
  871.         option = setKernelParamToLine
  872.             (option, "splash", "silent");
  873.         s["append"] = option;
  874.     }
  875.     else if (haskey (s, "append")
  876.         && contains (BootCommon::update_section_types, type)
  877.        && ! contains (recreated, label))
  878.     {
  879.         string option = s["append"]:"";
  880.         if (type != "linux" || ! linux_resume_added)
  881.         {
  882.         string resume
  883.             = BootCommon::getKernelParamFromLine (
  884.             option, "resume");
  885.         if (resume != "false")
  886.         {
  887.             y2milestone ("Updating resume device of section %1", label);
  888.             resume = BootCommon::UpdateDevice (resume);
  889.             option = BootCommon::setKernelParamToLine (
  890.             option, "resume", resume);
  891.         }
  892.         }
  893.         s["append"] = option;
  894.     }
  895.     return s;
  896.     });
  897.     sections = out;
  898. }
  899.  
  900. /**
  901.  * Update global options of bootloader
  902.  * modifies internal sreuctures
  903.  */
  904. global void UpdateGlobals () {
  905.     BootCommon::globals["timeout"] = "8";
  906.     list<string> s1_devs
  907.     = splitstring (BootCommon::globals["stage1_dev"]:"", ",");
  908.     s1_devs = maplist (string d, s1_devs, {
  909.     return UpdateDevice (d);
  910.     });
  911.     BootCommon::globals["stage1_dev"] = mergestring (s1_devs, ",");
  912.     BootCommon::globals["gfxmenu"] = "/boot/message";
  913. }
  914.  
  915. /**
  916.  * Update the device map according to changed device names
  917.  * Read device map and store it in internal structures
  918.  */
  919. global void UpdateDeviceMap () {
  920.     device_mapping = mapmap (string unix, string fw, device_mapping, {
  921.     y2milestone ("Updating device in devmap entry %1 -> %2",
  922.         unix, fw);
  923.     unix = BootCommon::UpdateDevice (unix);
  924.     return $[ unix : fw ];
  925.     });
  926.     y2milestone ("Updated device map: %1", device_mapping);
  927. }
  928.  
  929. /**
  930.  * Filter sections, remove those pointing to unexistent image
  931.  * @param path_prefix string prefix to be added to kernel path
  932.  * @param relative_path_prefix prefix to be added to relative kernel
  933.  *  paths (without leading slash)
  934.  */
  935. global void RemoveUnexistentSections (string path_prefix,
  936.     string relative_path_prefix)
  937. {
  938.     string defaultv = globals["default"]:"";
  939.     string first = nil;
  940.     BootCommon::sections = filter (map<string,any> s, BootCommon::sections, {
  941.     string label = s["name"]:"";
  942.  
  943.     // do not touch the wildcard section
  944.     if (regexpmatch (s["kernel"]:"", ".+\\-\\*"))
  945.     {
  946.         if (first == nil)
  947.         first = label;
  948.         return true;
  949.     }
  950.  
  951.     string type = s["original_name"]:"";
  952.         if (label == "")
  953.     {
  954.         y2warning ("Removing section with empty title");
  955.         if (label == defaultv)
  956.         defaultv = nil;
  957.         return false;
  958.     }
  959.     // FIXME the following check makes sense for all sections`
  960.     if (! contains (["linux", "failsafe", "memtest86", "xen"], type))
  961.     {
  962.         if (first == nil)
  963.         first = label;
  964.         return true;
  965.     }
  966.     string kernel = s["kernel"]:"";
  967.     if (kernel == "")
  968.     {
  969.         if (first == nil)
  970.         first = label;
  971.         return true;
  972.     }
  973.     if (substring (kernel, 0, 1) == "/")
  974.     {
  975.         kernel = path_prefix + kernel;
  976.     }
  977.     else
  978.     {
  979.         if (relative_path_prefix == "")
  980.         return true;
  981.         kernel = relative_path_prefix + kernel;
  982.     }
  983.     if (SCR::Read (.target.size, kernel) == -1)
  984.     {
  985.         y2warning ("Removing section %1 with unexistent kernel %2",
  986.         label, kernel);
  987.         if (label == defaultv)
  988.         defaultv = nil;
  989.         return false;
  990.     }
  991.     if (first == nil)
  992.         first = label;
  993.     return true;
  994.     });
  995.     if (defaultv == nil)
  996.     defaultv = first;
  997.     globals["default"] = defaultv;
  998. }
  999.  
  1000. /**
  1001.  * Remove or add initrd option if needed, update append option if some
  1002.  *  parameters were changed
  1003.  */
  1004. global void UpdateInitrdLine () {
  1005.     sections = maplist (map<string,any> s, sections, {
  1006.         string initrd = s["initrd"]:"";
  1007.     string title = s["name"]:"";
  1008.     string type = s["original_name"]:"";
  1009.  
  1010.     // do not touch the wildcard section
  1011.     if (regexpmatch (s["initrd"]:"", ".+\\-\\*"))
  1012.     {
  1013.         return s;
  1014.     }
  1015.  
  1016.         if ((type == "linux" || type == "failsafe")
  1017.         && write_settings["insert_initrd"]:false)
  1018.         {
  1019.         s["initrd"] = BootArch::InitrdImage ();
  1020.         }
  1021.     else if (type == "xen" && write_settings["insert_initrd"]:false)
  1022.     {
  1023.         if (BootCommon::UsingXenPae())
  1024.             s["initrd"] = "/boot/initrd-xenpae";
  1025.         else
  1026.             s["initrd"] = "/boot/initrd-xen";
  1027.     }
  1028.         if (initrd != "")
  1029.         {
  1030.         // check for initrd in GRUB format, keep them
  1031.         // FIXME change the check for leading slash
  1032.         if (getLoaderType (false) == "grub"
  1033.         && substring (initrd, 0, 1) == "(")
  1034.         {
  1035.         return s;
  1036.         }
  1037.         if (-1 == SCR::Read (.target.size, initrd) && haskey (s, "initrd"))
  1038.         s = remove (s, "initrd");
  1039.         }
  1040.     return s;
  1041.     });
  1042. }
  1043.  
  1044. /**
  1045.   * Update append option if some parameters were changed
  1046.   */
  1047. global void UpdateAppend () {
  1048.     sections = maplist (map<string,any> s, sections, {
  1049.     string type = s["original_name"]:"";
  1050.         if ((type == "linux" || type == "wildcard" || type == "global")
  1051.         && s["append"]:nil != nil
  1052.         && Stage::initial ())
  1053.     {
  1054.         s["append"] = UpdateKernelParams (s["append"]:"");
  1055.     }
  1056.         return s;
  1057.     });
  1058.     if (haskey (globals, "append"))
  1059.     {
  1060.     globals["append"] = UpdateKernelParams (globals["append"]:"");
  1061.     }
  1062. }
  1063.  
  1064. /**
  1065.  * Update the gfxboot/message/... line if exists
  1066.  */
  1067. global void UpdateGfxMenu () {
  1068.     string message = globals["gfxmenu"]:"";
  1069.     if (message != "")
  1070.     {
  1071.     if (-1 == SCR::Read (.target.size, message))
  1072.     {
  1073.         globals = remove (globals, "gfxmenu");
  1074.     }
  1075.     }
  1076.  
  1077. }
  1078.  
  1079. /**
  1080.  * Generate device map proposal, store it in internal variables
  1081.  */
  1082. // FIXME: move that funbction out here, as it is grub only
  1083. global void ProposeDeviceMap () {
  1084.     device_mapping = $[];
  1085.  
  1086.     map<string,map> targetMap = $[];
  1087.     if (Mode::config ())
  1088.     y2milestone ("Skipping device map proposing in Config mode");
  1089.     else
  1090.     targetMap = (map<string,map>)Storage::GetTargetMap();
  1091.  
  1092. //FIXME: This is not the final solution, we must be able to handle both,
  1093. //FakeRAIDs _and_ single disks.
  1094.  
  1095.     // Search in complete target map for "CT_DMRAID" types
  1096.     map <string, map> DMTargetMap = filter (string k, map v, targetMap, {
  1097.     return v["type"]:`CT_UNKNOWN==`CT_DMRAID;
  1098.     });
  1099.  
  1100.     // If no dmraids are present, search in complete target map for "CT_DISK"
  1101.     // types. This should be the "normal" case.
  1102.     if ( size(DMTargetMap) == 0 ) {
  1103.     targetMap = filter (string k, map v, targetMap, {
  1104.         return v["type"]:`CT_UNKNOWN==`CT_DISK;
  1105.         });
  1106.     } else {
  1107.     // If dmraid(s) are found, only use them
  1108.  
  1109.     // Get all available dmraids in the proper BIOS order
  1110.     map out = (map) SCR::Execute (.target.bash_output, "dmraid -s -c");
  1111.  
  1112.     if ( out["exit"]:0 == 0 && out["stdout"]:"" != "" ) {
  1113.         list<string> DMRaid_devices_in_BIOS_order =
  1114.         splitstring (out["stdout"]:"", "\n");
  1115.  
  1116.         // Remove last list entry (because it is empty)
  1117.         integer index_empty = size(DMRaid_devices_in_BIOS_order) - 1;
  1118.         DMRaid_devices_in_BIOS_order =
  1119.         remove(DMRaid_devices_in_BIOS_order, index_empty);
  1120.  
  1121.         integer DMRaidBiosID = 128;
  1122.  
  1123.         // FIXME: This code only work when YaST2-Storage either assigns
  1124.         // BIOS-IDs to all available DMRaid devices or to none of them.
  1125.         // Probably it's best to throw this code away once YaST2-Storage
  1126.         // assigns BIOS-IDs to all available DMRaid devices.
  1127.  
  1128.         // Assign BIOS-IDs to DMRaid devices in the Target Map in BIOS
  1129.         // order
  1130.         DMTargetMap = listmap (string DMRaidDevice, DMRaid_devices_in_BIOS_order, {
  1131.         DMRaidDevice = "/dev/mapper/" + DMRaidDevice;
  1132.         if (haskey(DMTargetMap, DMRaidDevice)) {
  1133.             if ( !haskey(DMTargetMap[DMRaidDevice]:$[], "bios_id") ) {
  1134.             DMTargetMap[DMRaidDevice, "bios_id"] = tohexstring(DMRaidBiosID);
  1135.             DMRaidBiosID = DMRaidBiosID + 1;
  1136.             }
  1137.             return $[ DMRaidDevice : DMTargetMap[DMRaidDevice]:$[] ];
  1138.         }
  1139.         });
  1140.         y2milestone("sorted (according to BIOS) DMTargetMap: %1", DMTargetMap);
  1141.     } else {
  1142.         y2error ("Command: \"dmraid -s -c\" failed, no sorting of DMTargetMap.");
  1143.     }
  1144.  
  1145.     targetMap = DMTargetMap;
  1146.     }
  1147.  
  1148.     y2milestone ("Target map: %1", targetMap);
  1149.  
  1150.     // add devices with known bios_id
  1151.     // collect BIOS IDs which are used
  1152.     map ids = $[];
  1153.     foreach (string target_dev, map target, targetMap, {
  1154.         string bios_id = target["bios_id"]:"";
  1155.         if (bios_id != "")
  1156.         {
  1157.             integer index = tointeger (bios_id) - tointeger ("0x80");
  1158.             string grub_dev = sformat ("hd%1", index);
  1159.         device_mapping[target_dev] = grub_dev;
  1160.             ids[index] = true;
  1161.         }
  1162.     });
  1163.     // and guess other devices
  1164.     // don't use already used BIOS IDs
  1165.     foreach (string target_dev, map target, targetMap, {
  1166.         string bios_id = target["bios_id"]:"";
  1167.         if (bios_id == "")
  1168.         {
  1169.             integer index = 0;
  1170.             while (ids[index]:false)
  1171.                 index = index + 1;
  1172.             string grub_dev = sformat ("hd%1", index);
  1173.         device_mapping[target_dev] = grub_dev;
  1174.             ids[index] = true;
  1175.         }
  1176.     });
  1177.     if (StorageDevices::FloppyPresent)
  1178.     device_mapping[StorageDevices::FloppyDevice] = "fd0";
  1179.     y2milestone ("Detected device mapping: %1", device_mapping);
  1180. }
  1181.  
  1182. /**
  1183.  * Get the order of disks according to BIOS mapping
  1184.  * @return a list of all disks in the order BIOS sees them
  1185.  */
  1186. global list<string> DisksOrder () {
  1187.     if (device_mapping == nil || size (device_mapping) == 0)
  1188.     {
  1189.     ProposeDeviceMap ();
  1190.     }
  1191.     map<string,string> devmap_rev = mapmap (string k, string v,
  1192.     BootCommon::device_mapping,
  1193.     {
  1194.     return $[ v : k ];
  1195.     });
  1196.     devmap_rev = filter (string k, string v, devmap_rev, {
  1197.     return substring (k, 0, 2) == "hd";
  1198.     });
  1199.     list<string> order = maplist (string k, string v, devmap_rev, {
  1200.     return v;
  1201.     });
  1202.     return order;
  1203. }
  1204.  
  1205. /**
  1206.  * Get the summary of disks order for the proposal
  1207.  * @return string a line for the summary (or nil if not intended to be shown)
  1208.  */
  1209. global string DiskOrderSummary () {
  1210.     list<string> order = DisksOrder ();
  1211.     string ret = nil;
  1212.     if (size (order) > 1)
  1213.     {
  1214.     ret = sformat (
  1215.         // part of summary, %1 is a list of hard disks device names
  1216.         _("Order of Hard Disks: %1"),
  1217.         mergestring (order, ", "));
  1218.     }
  1219.     return ret;
  1220. }
  1221.  
  1222.  
  1223. } // EOF
  1224.  
  1225. /*
  1226.  * Local variables:
  1227.  *     mode: ycp
  1228.  *     mode: font-lock
  1229.  *     mode: auto-fill
  1230.  *     indent-level: 4
  1231.  *     fill-column: 78
  1232.  * End:
  1233.  */
  1234.