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 / misc.ycp < prev    next >
Text File  |  2006-11-29  |  53KB  |  1,762 lines

  1. /**
  2.  * File:
  3.  *      include/bootloader/routines/misc-common.ycp
  4.  *
  5.  * Module:
  6.  *      Bootloader installation and configuration
  7.  *
  8.  * Summary:
  9.  *      Miscelaneous functions for bootloader configuration and installation
  10.  *
  11.  * Authors:
  12.  *      Jiri Srain <jsrain@suse.cz>
  13.  *      Olaf Dabrunz <od@suse.de>
  14.  *
  15.  * $Id: misc.ycp 34298 2006-11-13 12:02:16Z jplack $
  16.  *
  17.  * WARNING:
  18.  *      To be included to BootCommon.ycp only, requires function
  19.  *      getLoaderType to avoid include-import cycle
  20.  *      Use import "BootCommon" instead.
  21.  */
  22.  
  23.  
  24.  
  25. {
  26.  
  27.     textdomain "bootloader";
  28.     import "Mode";
  29.     import "Stage";
  30.  
  31.     import "Storage";
  32.     import "StorageDevices";
  33.     import "Report";
  34.     import "Kernel";
  35.     import "Misc";
  36.     import "ProductFeatures";
  37.     import "Directory";
  38.  
  39. // bootloader attributes handling functions
  40.  
  41.     global define map<string, integer> Md2Partitions (string md_device);
  42.     global define void DetectDisks ();
  43.  
  44.     /**
  45.       * Get packages needed by specified bootloader
  46.       * maybe should be moved to bootloader specific modules
  47.       * @param bootloader string name of bootloader
  48.       * @return a list of required packages names
  49.       */
  50.     global define list<string> getBootloaderPackages (string bootloader) ``{
  51.     return bootloader_attribs[bootloader, "required_packages"]:[];
  52.     }
  53.  
  54.     /**
  55.       * return printable name of bootloader
  56.       * @param bootloader string bootloader type internal string
  57.       * @param mode symbol `combo or `summary (because of capitalization)
  58.       * @return string printable bootloader name
  59.       */
  60.     global define string getLoaderName (string bootloader, symbol mode) ``{
  61.     if (bootloader == "none")
  62.     {
  63.         return mode == `summary
  64.         // summary string
  65.         ? _("Do not install any boot loader")
  66.         // combo box item
  67.         : _("Do Not Install Any Boot Loader");
  68.     }
  69.     if (bootloader == "default")
  70.     {
  71.         return mode == `summary
  72.         // summary string
  73.         ? _("Install the default boot loader")
  74.         // combo box item
  75.         : _("Install Default Boot Loader");
  76.     }
  77.     string fallback_name = mode == `summary
  78.         // summary string
  79.         ? _("Boot loader")
  80.         // combo box item
  81.         : _("Boot Loader");
  82.     // fallback bootloader name, keep short
  83.     return bootloader_attribs[bootloader, "loader_name"]:fallback_name;
  84.     }
  85.  
  86.     /**
  87.       * Get value of specified boolean bootloader attribute
  88.       * @param attrib string attribute name
  89.       * @return boolean value of attribute
  90.       */
  91.     global define boolean getBooleanAttrib (string attrib) ``{
  92.     return current_bootloader_attribs[attrib]:false;
  93.     }
  94.  
  95.     /**
  96.       * Get value of specified bootloader attribute
  97.       * @param attrib string attribute name
  98.       * @param defaultv any default value of the attribute (if not found)
  99.       * @return any value of attribute
  100.       */
  101.     global define any getAnyTypeAttrib (string attrib, any defaultv) ``{
  102.         return current_bootloader_attribs[attrib]:defaultv;
  103.     }
  104.  
  105. /**
  106.  * Get index of a section specified by name
  107.  * @param name string section name
  108.  * @return integer index of the section or nil if not found
  109.  */
  110. global integer getSectionIndex (string name) {
  111.     integer index = -1;
  112.     integer sectnum = nil;
  113.     foreach (map<string,any> s, sections, {
  114.     index = index + 1;
  115.     if (s["name"]:"" == name)
  116.         sectnum = index;
  117.     });
  118.     return sectnum;
  119. }
  120.  
  121.     /**
  122.       * Get map where to store kernel parameters
  123.       * @return map describing where to store which kernel parameter
  124.       */
  125.     global define map getKernelParamKeys () ``{
  126.         return current_bootloader_attribs["kernel_params"]:$[];
  127.     }
  128.  
  129.  
  130. // other misc functions
  131.  
  132. /**
  133.  * Generates unused section label for new section
  134.  * @return string label for new section
  135.  */
  136. global string getFreeSectionLabel () {
  137.     integer index = 1;
  138.     list<string> existing = [];
  139.     foreach (map<string,any> s, sections, {
  140.     existing = add (existing, s["name"]:"");
  141.     list<string> aliases = splitstring (s["aliases"]:"", ";");
  142.     foreach (string a, aliases, {
  143.         existing = add (existing, a);
  144.     });
  145.     });
  146.     while (true)
  147.     {
  148.     string title = sformat ("section %1", index);
  149.     if (! contains (existing, title))
  150.         return title;
  151.     index = index + 1;
  152.     }
  153. }
  154.  
  155.     /**
  156.       * Get the list of installed floppy drives
  157.       * @return a list of floppy devices
  158.       */
  159.     global define list<string> getFloppyDevices () ``{
  160.         if (floppy_devices == nil)
  161.         {
  162.             list<map> floppies = (list<map>) SCR::Read (.probe.floppy);
  163.             floppies = filter (map f, floppies,
  164.                 ``(f["model"]:"Floppy Disk" == "Floppy Disk"));
  165.             floppy_devices = maplist (map f, floppies,
  166.         ``(f["dev_name"]:""));
  167.             floppy_devices = filter (string f, floppy_devices, ``(f != ""));
  168.         }
  169.     return floppy_devices;
  170.     }
  171.  
  172.  
  173.     /**
  174.       * Update the text of countdown widget
  175.       * @param bootloader string printable name of used bootloader
  176.       */
  177.     global define void updateTimeoutPopupForFloppy (string bootloader) ``{
  178.     if (Mode::normal ())
  179.         return;
  180.  
  181.         string confirm_boot_msg = Misc::boot_msg;
  182.         // data saved to floppy disk
  183.         string msg = sformat (
  184.             // popup, %1 is bootloader name
  185.             _("The %1 boot sector has been written to the floppy disk."),
  186.             bootloader);
  187.         msg = msg + "\n";
  188.     // always hard boot
  189.         // If LILO was written on floppy disk and we need
  190.         // to do a hard reboot (because a different kernel
  191.         // was installed), tell the user to leave the floppy
  192.         // inserted.
  193.         msg = msg
  194.             // popup - continuing
  195.             + _("Leave the floppy disk in the drive.");
  196.  
  197.         if ( size (confirm_boot_msg) > 0 )
  198.         {
  199.             msg = msg + "\n" + confirm_boot_msg;
  200.         }
  201.         Misc::boot_msg = msg;
  202.     }
  203.  
  204.     /**
  205.       * List of all supported bootloaders
  206.       */
  207.     global list<string> bootloaders = ["lilo", "grub", "elilo", "milo", "silo",
  208.     "aboot", "zipl", "ppc", "mips"];
  209.  
  210.     /**
  211.      * converts array of string to path
  212.      * @param strs list of string
  213.      * @return path whose components are taken from strs
  214.      */
  215.     global define path list2path(list<string> strs) ``{
  216.         path pth = .;
  217.         foreach(string e, strs, ``{
  218.             pth = add(pth, e);
  219.         });
  220.         return pth;
  221.     }
  222.  
  223.     /**
  224.      * Converts a "/dev/disk/by-" device name to the corresponding kernel
  225.      * device name, if a mapping for this name can be found in the map from
  226.      * yast2-storage. If the given device name is not a "/dev/disk/by-" device
  227.      * name, it is left unchanged. Also, if the information about the device
  228.      * name cannot be found in the target map from yast2-storage, the device
  229.      * name is left unchanged.
  230.      *
  231.      * @param dev string device name
  232.      * @return string kernel device name
  233.      */
  234.     global define string MountByDev2Dev(string dev) ``{
  235.     y2milestone ("MountByDev2Dev: %1", dev);
  236.  
  237.     if (!regexpmatch(dev, "^/dev/disk/by-"))
  238.         return dev;
  239.  
  240.     map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
  241.  
  242.     // make a map: "type/identifier" -> kernel_device_name
  243.     // e.g.: "id/scsi-SATA_ST3120813AS_3LS09W21-part5" -> "/dev/sda2"
  244.     map<string,string> partitions = $[];
  245.     foreach (string k, map v, devices, ``{
  246.         foreach (map p, (list<map>)(v["partitions"]:[]), ``{
  247.         if ( haskey(p, "uuid") )
  248.         {
  249.             partitions["uuid/" + p["uuid"]:""] = p["device"]:dev;
  250.         }
  251.         if ( haskey(p, "label") )
  252.         {
  253.             partitions["label/" + p["label"]:""] = p["device"]:dev;
  254.         }
  255.         if ( haskey(p, "udev_id") )
  256.         {
  257.             partitions["id/" + p["udev_id", 0]:""] = p["device"]:dev;
  258.         }
  259.         if ( haskey(p, "udev_path") )
  260.         {
  261.             partitions["path/" + p["udev_path"]:""] = p["device"]:dev;
  262.         }
  263.         });
  264.     });
  265.     y2milestone ("MountByDev2Dev: partitions %1", partitions);
  266.  
  267.     // ident is "id/<id>", "path/<path>", "uuid/<uuid>" or "label/<label>"
  268.     string ident = regexpsub(dev, "^/dev/disk/by-(.*)", "\\1");
  269.     y2milestone ("MountByDev2Dev: ident %1", ident);
  270.     // if exists, get the kernel name, otherwise leave unchanged
  271.     string ret = partitions[ident]:dev;
  272.  
  273.     y2milestone ("MountByDev2Dev: ret %1", ret);
  274.     return ret;
  275.     }
  276.  
  277.     /**
  278.      * Converts a device name to the corresponding device name it should be
  279.      * mounted by, according to the "mountby" setting for the device from
  280.      * yast2-storage.
  281.      *
  282.      * @param dev string device name
  283.      * @return string device name according to "mountby"
  284.      */
  285.     global define string Dev2MountByDev(string dev) ``{
  286.     y2milestone ("Dev2MountByDev: %1", dev);
  287.     map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
  288.  
  289.     // make a map: "/dev/hda1" -> info_map_for_this_partition
  290.     map<string,map> partitions = $[];
  291.     foreach (string k, map v, devices, ``{
  292.         foreach (map p, (list<map>)(v["partitions"]:[]), ``{
  293.         partitions[p["device"]:""] = p;
  294.         });
  295.     });
  296.     y2debug ("Dev2MountByDev: partitions %1", partitions);
  297.  
  298.     string ret = dev;
  299.  
  300.     symbol mountby = `device;
  301.     mountby = (symbol) partitions[dev, "mountby"]:nil;
  302.     if ( mountby == `uuid )
  303.     {
  304.         ret = sformat ("/dev/disk/by-uuid/%1", partitions[dev, "uuid"]:"");
  305.     } else if ( mountby == `label )
  306.     {
  307.         ret = sformat ("/dev/disk/by-label/%1", partitions[dev, "label"]:"");
  308.     } else if ( mountby == `id )
  309.     {
  310.         ret = sformat ("/dev/disk/by-id/%1", partitions[dev, "udev_id", 0]:"");
  311.     } else if ( mountby == `path )
  312.     {
  313.         ret = sformat ("/dev/disk/by-path/%1", partitions[dev, "udev_path"]:"");
  314.     }
  315.  
  316.     y2milestone ("Dev2MountByDev: ret %1", ret);
  317.     return ret;
  318.     }
  319.  
  320.     /**
  321.      * Returns list of partitions with "mount by" hints. Goes through the list
  322.      * of partitions passed as a parameter and creates a list of partitions with
  323.      * hints according to the current partitioning requested from
  324.      * yast2-storage. To be used in a combobox or menu.
  325.      *
  326.      * @param parts_to_get list<string> partitions to list
  327.      * @return a list of strings containing a partition name and a hint (if applicable)
  328.      */
  329.     global define list<string> getHintedPartitionList(list<string> parts_to_get) ``{
  330.     y2milestone ("getHintedPartitionList: %1", parts_to_get);
  331.     map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
  332.  
  333.     // make a map: "/dev/hda1" -> info_map_for_this_partition
  334.     map<string,map> partitions = $[];
  335.     foreach (string k, map v, devices, ``{
  336.         foreach (map p, (list<map>)(v["partitions"]:[]), ``{
  337.         partitions[p["device"]:""] = p;
  338.         });
  339.     });
  340.     y2milestone ("getHintedPartitionList: partitions %1", partitions);
  341.  
  342.     symbol mountby = `device;
  343.     list<string> ret = maplist (string dev, parts_to_get, ``{
  344.         mountby = (symbol) partitions[dev, "mountby"]:nil;
  345.         if ( mountby == `uuid )
  346.         {
  347.         return sformat ("%1 (mount by UUID: %2)", dev,
  348.                 partitions[dev, "uuid"]:nil != nil ?
  349.                 partitions[dev, "uuid"]:"" :
  350.                 "<UUID to be created later during format>");
  351.         } else if ( mountby == `label )
  352.         {
  353.         return sformat ("%1 (mount by LABEL: %2)", dev, partitions[dev, "label"]:"");
  354.         } else if ( mountby == `id )
  355.         {
  356.         return sformat ("%1 (mount by ID: %2)", dev, partitions[dev, "udev_id", 0]:"");
  357.         } else if ( mountby == `path )
  358.         {
  359.         return sformat ("%1 (mount by PATH: %2)", dev, partitions[dev, "udev_path"]:"");
  360.         } else if ( mountby == nil || mountby == `device )
  361.         {
  362.         return dev;
  363.         }
  364.     });
  365.  
  366.     y2milestone ("getHintedPartitionList: ret %1", ret);
  367.     return ret;
  368.     }
  369.  
  370.     /**
  371.      * Returns list of partitions. Requests current partitioning from
  372.      * yast2-storage and creates list of partition for combobox, menu or other
  373.      * purpose.
  374.      * @param type symbol
  375.      *   `boot - for bootloader installation
  376.      *   `root - for kernel root
  377.      *   `boot_other - for bootable partitions of other systems
  378.      *   `all - all partitions
  379.      *   `parts_old - all partitions, except those what will be created
  380.      *      during isntallation
  381.      *   `deleted - all partitions deleted in current proposal
  382.      *   `kept - all partitions that won't be deleted, new created or formatted
  383.      *   `destroyed - all partition which are new, deleted or formatted
  384.      * @return a list of strings
  385.      */
  386.     global define list<string> getPartitionList(symbol type) ``{
  387.     y2milestone ("getPartitionList: %1", type);
  388.     map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
  389.     list<map> partitions = [];
  390.     foreach (string k, map v, devices, ``{
  391.         partitions = (list<map>)merge (partitions, (list<map>)(v["partitions"]:[]));
  392.     });
  393.     list<string> floppies = getFloppyDevices ();
  394.  
  395.     devices = filter (string k, map v, devices,
  396.                       ``(v["type"]:`CT_UNKNOWN != `CT_LVM));
  397.  
  398.     if (type == `boot || type == `boot_other)
  399.     {
  400.         devices = filter (string k, map v, devices,
  401.                           ``(v["type"]:`CT_UNKNOWN == `CT_DISK));
  402.     }
  403.     list<string> all_disks = (list<string>) maplist (string k, map v, devices, ``(k));
  404.  
  405.     if (type == `deleted)
  406.         return maplist (map x, filter (map p, partitions, ``(p["delete"]:false)),
  407.         ``{return x["device"]:"";
  408.         });
  409.     else if (type == `destroyed)
  410.     {
  411.         return maplist (map x, filter (map p, partitions, {
  412.             return p["delete"]:false || p["format"]:false
  413.             || p["create"]:false;
  414.         }),
  415.         {
  416.         return x["device"]:"";
  417.         });
  418.     }
  419.         partitions = filter (map p, partitions, ``(! p["delete"]:false));
  420.     list<string> ret = all_disks;
  421.     if (type == `boot_other || type == `root || type == `parts_old
  422.         || type == `kept)
  423.             ret = [];
  424.  
  425.     if (type == `boot)
  426.         {
  427.         partitions = filter (map p, partitions,
  428.                 ``(p["type"]:`primary == `primary
  429.             || p["type"]:`primary == `extended
  430.             || p["type"]:`primary == `logical
  431.             || p["type"]:`primary == `sw_raid));
  432.         // FIXME this checking is performed on 3 places, one function should
  433.         // be developed for it
  434.         partitions = filter (map p, partitions, {
  435.         symbol fs = (symbol)(p["used_fs"]:p["detected_fs"]:nil);
  436.         if (fs == `xfs)
  437.             return false;
  438.         return true;
  439.         });
  440.         }
  441.         else if (type == `root)
  442.         {
  443.             partitions = filter (map p, partitions,
  444.                     ``(p["type"]:`primary != `extended));
  445.         }
  446.     else if (type == `parts_old)
  447.     {
  448.         partitions = filter (map p, partitions, ``(! p["create"]:false));
  449.     }
  450.     else if (type == `kept)
  451.     {
  452.         partitions = filter (map p, partitions, {
  453.         return ! (p["create"]:false || p["format"]:false);
  454.         });
  455.     }
  456.         if (type != `all && type != `parts_old && type != `kept)
  457.         {
  458.             partitions = filter (map p, partitions,
  459.                 ``(p["fstype"]:"" != "Linux swap"));
  460.         }
  461.     if (type == `boot)
  462.     {
  463.         partitions = filter (map p, partitions,
  464.                 ``(p["fstype"]:"" == "Linux native"
  465.                     || p["fstype"]:"" == "Extended"
  466.             || p["fstype"]:"" == "Linux RAID"
  467.             || p["fstype"]:"" == "MD Raid"));
  468.     }
  469.         list<string>partition_names
  470.         = maplist (map p, partitions, ``((string)(p["device"]:"")));
  471.     partition_names = filter (string p, partition_names, ``(p != ""));
  472.         partition_names = (list<string>)merge (partition_names, floppies);
  473.         ret = (list<string>)union (ret, partition_names);
  474.     ret = (list<string>)toset (ret);
  475.     return (list<string>)ret;
  476.     }
  477.  
  478.     /**
  479.       * returns true if char is blank (newline, tab or space)
  480.       * @param s single char string
  481.       * @return boolean blank/non blank
  482.       */
  483.     global define boolean isBlank(string s) ``{
  484.     if (s == "\n" || s == "\t" || s == " ")
  485.     {
  486.         return true;
  487.     }
  488.     return false;
  489.     }
  490.  
  491.     /**
  492.       * removes trailing and leading blank chars from string.
  493.       *   eg: "  as df  " -> "as df"
  494.       * @param str string source string
  495.       * @return string stripped string
  496.       */
  497.     global define string strip(string str)``{
  498.     //emtpy  string
  499.     if (size(str) == 0) return "";
  500.  
  501.     integer bound = size(str);
  502.     integer first = 0;
  503.     integer last = size(str) - 1;
  504.     // find first non-blank char
  505.     while(first < bound && isBlank(substring(str, first, 1)))
  506.     {
  507.         first = first + 1;
  508.     }
  509.  
  510.     while(last >= 0 && isBlank(substring(str, last, 1)))
  511.     {
  512.         last = last - 1;
  513.     }
  514.     if (last >= first)
  515.     {
  516.         return substring(str, first, last - first + 1);
  517.     }
  518.     return "";
  519.     }
  520.  
  521.     /**
  522.       * replaces all occurences of 'from' to 'to' in src
  523.       * @param src input string
  524.       * @param from string to be replaced
  525.       * @param to string to be replaced by
  526.       * @return string modified string
  527.       */
  528.     global define string replaceAll(string src, string from, string to) ``{
  529.     if (from == "")
  530.     {
  531.         return src;
  532.     }
  533.  
  534.     list tokens = [];
  535.  
  536.     integer p = search(src, from);
  537.     while(p != nil)
  538.     {
  539.         tokens = add(tokens, substring(src, 0, p));
  540.         src = substring(src, p + size(from), size(src) - (p+size(from)));
  541.         p = search(src, from);
  542.     }
  543.     tokens = add(tokens, src);
  544.     return mergestring((list<string>)tokens, to);
  545.     }
  546.  
  547.     /**
  548.       * convert string values from agent representation to module representation
  549.       * 1) if string is quoted, quotes are removed
  550.       * 2) if string contains escaped quotes, they're unescaped
  551.       * @param val value to transform
  552.       * @return any transformed value
  553.       */
  554.     global define any mod2ui(any val) ``{
  555.     if (is(val, string))
  556.     {
  557.         string v = sformat("%1", val);
  558.         v = strip(v);
  559.         // remove leading and trailing quotes
  560.         if (substring(v, 0, 1) == "\""
  561.         && substring(v, size(v)-1, 1) == "\"")
  562.         {
  563.         v = substring(v, 1, size(v)-2);
  564.         }
  565.  
  566.         // unescape backslashes
  567.         v = replaceAll(v, "\\\\", "\\");
  568.  
  569.         // unescape quotes
  570.         v = replaceAll(v, "\\\"", "\"");
  571.         return v;
  572.     }
  573.     return val;
  574.     }
  575.  
  576.     /**
  577.       * returns list difference A \ B (items that are in A and are not in B)
  578.       * @param a list A
  579.       * @param b list B
  580.       * @return list see above
  581.       */
  582.     global define list difflist(list a, list b) ``{
  583.     return filter(any e, a, ``(!contains(b, e)));
  584.     }
  585.  
  586.     /**
  587.       * reverse of mod2ui
  588.       * @param val value to transform
  589.       * @return any transformed value
  590.       */
  591.     global define any ui2mod(any val) ``{
  592.     if (is(val, string))
  593.     {
  594.         string v = sformat("%1", val);
  595.         v = strip(v);
  596.         // if string contains backslashes, escape them
  597.         v = replaceAll(v, "\\", "\\\\");
  598.  
  599.         // if string contains quotes, escape them
  600.         v = replaceAll(v, "\"", "\\\"");
  601.         // if string contains spaces or equal sign or is empty, quote it
  602.  
  603.         if ((size(splitstring(v, " ")) >= 2 || size(v) == 0
  604.         || findfirstof(v, "=") != nil)
  605.             &&(getLoaderType (false) != "grub"))
  606.         {
  607.         v = "\"" + v + "\"";
  608.         }
  609.         return v;
  610.     }
  611.     return val;
  612.     }
  613.  
  614.  
  615.     /**
  616.       * returns modified list where items index1 and index2 are swapped.
  617.       * if indices are out of bounds, unmodified list is returned.
  618.       * @param input list
  619.       * @param index1 index of the first element
  620.       * @param index2 index og the second element
  621.       * @return list modified list
  622.       */
  623.     global define list swapItems(list input, integer index1, integer index2) ``{
  624.     if (index1 >= size(input) || index2 >= size(input)
  625.         || index1 < 0 || index2 < 0)
  626.     {
  627.         return input;
  628.     }
  629.     list output = [];
  630.     integer pos = -1;
  631.     output = maplist(any e, input, {
  632.         pos = pos + 1;
  633.         if (pos == index1)
  634.         {
  635.             return (any)(input[index2]:(any)$[]);
  636.         }
  637.         else if (pos == index2)
  638.         {
  639.             return (any)(input[index1]:(any)$[]);
  640.         }
  641.         return e;
  642.     });
  643.     return output;
  644.     }
  645.  
  646.     /**
  647.       * translate filename path (eg. /boot/kernel) to list of device
  648.       *  and relative path
  649.       * @param fullpth string fileststem path (eg. /boot/vmlinuz)
  650.       * @return a list containing device and relative path,
  651.       *  eg. ["/dev/hda1", "/vmlinuz"]
  652.       */
  653.     global define list<string> splitPath (string fullpth) ``{
  654. // UGHLY HACK because of testsuites
  655.     map<string,list> mountpoints = $[];
  656.     if (Mode::test ())
  657.         mountpoints = $["/" : ["/dev/hda2"], "/boot" : ["/dev/hda1"]];
  658.     else
  659.         mountpoints = (map<string,list>)Storage::GetMountPoints();
  660.     string dev = "";
  661.     string mp = "";
  662.     integer max = 0;
  663.     //
  664.     // FIXME: this is broken code, implement a proper prefix match!! see below
  665.     foreach (string k, list v, mountpoints, ``{
  666.         if (k != "swap" && issubstring (fullpth, k) && size (k) > max)
  667.         {
  668.             max = size (k);
  669.             dev = v[0]:"";
  670.             mp = k;
  671.         }
  672.     });
  673.     if (mp == "")
  674.         return [];
  675.  
  676.     // FIXME: pth will be wrong for fullpth=='(hd0,1)/boot/vmlinux' !!
  677.     string pth = substring (fullpth, size (mp));
  678.     if (substring (pth, 0, 1) != "/")
  679.         pth = "/" + pth;
  680.     return [dev, pth];
  681.     }
  682.  
  683.     /**
  684.       * translate list of device and relative path
  685.       *  to filename path (eg. /boot/kernel)
  686.       * @param devpth list of two strings, first for device name, second for
  687.       *  relative path (eg. ["/dev/hda1", "/vmlinuz"])
  688.       * @return string fileststem path (eg. /boot/vmlinuz)
  689.       */
  690.     global define string mergePath (list<string> devpth) ``{
  691. // UGHLY HACK because of testsuites
  692.         map<string,list> mountpoints = $[];
  693.     if (Mode::test ())
  694.         mountpoints = $["/" : ["/dev/hda2"], "/boot" : ["/dev/hda1"]];
  695.     else
  696.             mountpoints = (map<string,list>)Storage::GetMountPoints();
  697.     string ret = "/dev/null";
  698.     if (substring (devpth[1]:"", 0, 1) != "/")
  699.         devpth[1] = "/" + devpth[1]:"";
  700.         foreach (string k, list v, mountpoints, ``{
  701.         if (k != "swap" && v[0]:"" == devpth[0]:"")
  702.         {
  703.         if (substring (k, size (k) - 1, 1) == "/")
  704.             k = substring (k, 0, size (k) - 1);
  705.         ret = k + devpth[1]:"";
  706.         }
  707.     });
  708.     return ret;
  709.     }
  710.  
  711.     /**
  712.       * If device is part of RAID (md), then return first of its members
  713.       * otherwise return the same as argument
  714.       * @param device string device of the RAID
  715.       * @return string first member of the RAID
  716.       */
  717.     global define string getDeviceOfRaid (string device) ``{
  718.     // get list of all partitions (not marked to be deleted)
  719.     map<string,map> tm = (map<string,map>)Storage::GetTargetMap ();
  720.     list<map> partitions = [];
  721.  
  722.     foreach ( string dev, map disk, tm, ``{
  723.         if( Storage::IsRealDisk( disk ) )
  724.         {
  725.         list<map> l = (list<map>)filter( map p, disk["partitions"]:[],
  726.             ``(p["delete"]:false==false) );
  727.         partitions = (list<map>)merge (partitions, l);
  728.             }
  729.         });
  730.  
  731.     // filter partitions to relevant list according to raid name
  732.     list<map> md_list = filter (map e, partitions, ``(e["used_by"]:"" == substring(device,5)));
  733.     // get the devices
  734.     list<string> dev_list = maplist (map e, md_list, ``(e["device"]:""));
  735.     dev_list = filter (string d, dev_list, ``(d != ""));
  736.     if (size (dev_list) > 0)
  737.     {
  738.         dev_list = sort (dev_list);
  739.         return dev_list[0]:"";
  740.     }
  741.     return device;
  742.     }
  743.  
  744.     /**
  745.       * Get bootloader device for specified location
  746.       * @return string device name
  747.       */
  748.     global define string GetBootloaderDevice () ``{
  749.         if (BootCommon::selected_location == "mbr")
  750.             return BootCommon::mbrDisk;
  751.         if (BootCommon::selected_location == "boot")
  752.         return BootCommon::BootPartitionDevice;
  753.         if (BootCommon::selected_location == "root")
  754.         return  BootCommon::RootPartitionDevice;
  755.         if (BootCommon::selected_location == "floppy")
  756.             return StorageDevices::FloppyDevice;
  757.         if (BootCommon::selected_location == "mbr_md")
  758.         return "mbr_md";
  759.     if (BootCommon::selected_location == "none")
  760.         return "/dev/null";
  761.         return BootCommon::loader_device;
  762.     }
  763.  
  764.  
  765. /**
  766.  * Check if installation to floppy is performed
  767.  * @return true if installing bootloader to floppy
  768.  */
  769. global boolean InstallingToFloppy () {
  770.     boolean ret = false;
  771.     if (loader_device == StorageDevices::FloppyDevice)
  772.     ret = true;
  773.     else if (contains (getFloppyDevices (), loader_device))
  774.     ret = true;
  775.     y2milestone ("Installing to floppy: %1", ret);
  776.     return ret;
  777. }
  778.  
  779.     /**
  780.       *
  781.       * @return true if installing bootloader to floppy
  782.       */
  783.     global define boolean installingToFloppy () ``{
  784.     if (BootCommon::loader_device == StorageDevices::FloppyDevice)
  785.         return true;
  786.     if (contains (getFloppyDevices (), BootCommon::loader_device))
  787.         return true;
  788.     return false;
  789.     }
  790.  
  791. /**
  792.  * Get the list of particular kernel parameters
  793.  * @param line string the whole kernel command line
  794.  * @return a list of the kernel parameters split each separaterlly
  795.  */
  796. global list<string> ListKernelParamsInLine (string line) {
  797. // FIXME this function is really similar to code in Kernel.ycp
  798.     list<string> cmdlist = [];
  799.     integer parse_index = 0;
  800.     boolean in_quotes = false;
  801.     boolean after_backslash = false;
  802.     string current_param = "";
  803.     while (parse_index < size (line))
  804.     {
  805.     string current_char = substring (line, parse_index, 1);
  806.     if (current_char == "\"" && ! after_backslash)
  807.         in_quotes = ! in_quotes;
  808.     if (current_char == " " && ! in_quotes)
  809.     {
  810.         cmdlist = add (cmdlist, current_param);
  811.         current_param = "";
  812.     }
  813.     else
  814.         current_param = current_param + current_char;
  815.     if (current_char == "\\")
  816.         after_backslash = true;
  817.     else
  818.         after_backslash = false;
  819.     parse_index = parse_index + 1;
  820.     }
  821.     cmdlist = add (cmdlist, current_param);
  822.     cmdlist = maplist (string c, cmdlist, {
  823.     if (regexpmatch (c, "^[^=]+="))
  824.         c = regexpsub (c, "^([^=]+)=", "\\1");
  825.     return c;
  826.     });
  827.     return cmdlist;
  828.  
  829. }
  830.  
  831.     /**
  832.       * get kernel parameter from kernel command line
  833.       * @param line string original line
  834.       * @param key string parameter key
  835.       * @return string value, "false" if not present,
  836.       *   "true" if present key without value
  837.       */
  838.     global define string getKernelParamFromLine (string line, string key) ``{
  839.     // FIXME this doesn't work with quotes and spaces
  840.     string res = "false";
  841.     list<string> params = splitstring (line, " ");
  842.     params = filter (string p, params, ``(p != ""));
  843.     foreach (string p, params, ``{
  844.         list<string> l = (list<string>) filter (string e, splitstring (p, "="), ``(e != " " && e != ""));
  845.         if (l[0]:"" == key)
  846.             res = l[1]:"true";
  847.     });
  848.     return res;
  849.     }
  850.  
  851.     /**
  852.       * set kernel parameter to GRUB command line
  853.       * @param line string original line
  854.       * @param key string parameter key
  855.       * @param value string value, "false" to remove key,
  856.       *   "true" to add key without value
  857.       * @return string new kernel command line
  858.       */
  859.     global define string setKernelParamToLine
  860.     (string line, string key, string value)
  861.     ``{
  862.     // FIXME this doesn't work with quotes and spaces
  863.     list<string> params = splitstring (line, " ");
  864.     params = filter (string p, params, ``(p != ""));
  865.     boolean done = false;
  866.     // count occurences of every parameter
  867.     map<string,integer> occurences = $[];
  868.     foreach (string p, params, ``{
  869.         list<string> l = filter (string e, splitstring (p, "="), ``(e != " " && e != ""));
  870.         string k = l[0]:"";
  871.         occurences[k] = occurences[k]:0 + 1;
  872.     });
  873.     params = maplist (string p, params, ``{
  874.         list<string> l = filter (string e, splitstring (p, "="), ``(e != " " && e != ""));
  875.         string k = l[0]:"";
  876.         if (k == key)
  877.         {
  878.         if (value == "false")
  879.         {
  880.             return "";
  881.         }
  882.         else if (occurences[k]:0 <= 1)
  883.         {
  884.                 done = true;
  885.             if (value == "true")
  886.             {
  887.             return key;
  888.             }
  889.             else if (value != "false")
  890.             {
  891.             return sformat ("%1=%2", key, value);
  892.             }
  893.         }
  894.         else
  895.         {
  896.             occurences[k] = occurences[k]:0 - 1;
  897.             return "";
  898.         }
  899.         }
  900.         return p;
  901.     });
  902.     if (! done)
  903.     {
  904.         if (value == "true")
  905.         {
  906.             params = add (params, key);
  907.         }
  908.         else if (value != "false")
  909.         {
  910.             params = add (params, sformat ("%1=%2", key, value));
  911.         }
  912.     }
  913.     params = filter (string p, params, ``(p != ""));
  914.     line = mergestring (params, " ");
  915.     return line;
  916.     }
  917.  
  918.     /**
  919.       * remove blanks from section name and replace them with _
  920.       * when not using GRUB
  921.       * @param original string
  922.       * @return string fixed string
  923.       */
  924.     global define string removeBlanks (string original) {
  925.     if (getLoaderType (false) == "grub")
  926.         return original;
  927.     // do not allow empty labels
  928.     while (size (original) > 1 && substring (original, 0, 1) == " ")
  929.     {
  930.         original = substring (original, 1);
  931.     }
  932.     while (size (original) > 1
  933.         && substring (original, size (original) -1, 1) == " ")
  934.     {
  935.         original = substring (original, 0, size (original) - 1);
  936.     }
  937.     if (size (original) > 15)
  938.        original = substring (original, 0, 15);
  939.     return replaceAll (original, " ", "_");
  940.     }
  941.  
  942.     /**
  943.       * Filter list to only entries which are present as keys in map
  944.       * @param order list (ordered) of settings
  945.       * @param widgets map of present widgets
  946.       * @param index of selected option
  947.       * @return map containing new index of selected option and list of options
  948.       *  where options that aren't ordered precede ordered options
  949.       */
  950.     global define map filterOrder (list order, map widgets, integer index) ``{
  951.     list ordering = [];
  952.     list unordering = [];
  953.     integer new_index = -1;
  954.     integer order_count = 0;
  955.     integer sorted_cound = 0;
  956.     foreach (map o, (list<map>)order, ``{
  957.         boolean ord_option = widgets[o["key"]:"", "table", "ordering"]:true;
  958.         if (substring (o["key"]:"", 0, 2) == "__")
  959.         ord_option = false;
  960.         if (ord_option)
  961.         {
  962.         ordering = add (ordering, o);
  963.         order_count = order_count + 1;
  964.         }
  965.         else
  966.         {
  967.         unordering = add (unordering, o);
  968.         }
  969.         if (index == 0)
  970.         {
  971.         new_index = order_count - 1;
  972.         }
  973.         index = index - 1;
  974.     });
  975.     new_index = new_index + size (unordering);
  976.     return $[
  977.         "items" : merge (unordering, ordering),
  978.         "index" : new_index,
  979.     ];
  980.     }
  981.  
  982.     /*
  983.      *  convert any value to an integer and return 0 for nonsense
  984.      */    
  985.     define integer myToInteger(any num_any) {
  986.     if (num_any == nil)
  987.             return 0;
  988.     if (is (num_any, integer))
  989.         return (integer)num_any;
  990.     if (is (num_any, string))
  991.         return (num_any == "") ? 0 : tointeger((string)num_any);
  992.     return 0;
  993.     } 
  994.  
  995.     /**
  996.       * Get partition which should be activated if doing it during bl inst.
  997.       * @param boot_partition string the partition holding /boot subtree
  998.       * @param loader_device string the device to install bootloader to
  999.       * @return a map $[ "dev" : string, "mbr": string, "num": any]
  1000.       *  containing device (eg. "/dev/hda4"), disk (eg. "/dev/hda") and
  1001.       *  partition number (eg. 4)
  1002.       */
  1003.     global define map<string,any> getPartitionToActivate (string boot_partition,
  1004.     string loader_device)
  1005.     {
  1006.     map p_dev = Storage::GetDiskPartition (loader_device);
  1007.     integer num = myToInteger( p_dev["nr"]:nil );
  1008.     string mbr_dev = p_dev["disk"]:"";
  1009.  
  1010.     // if bootloader is installed to /dev/md*
  1011.     // FIXME: use ::storage to detect md devices, not by name!
  1012.     if (substring (loader_device, 0, 7) == "/dev/md")
  1013.     {
  1014.         map<string,integer> md = Md2Partitions (BootCommon::loader_device);
  1015.         integer min = 128; // max. is 127
  1016.         string device = "";
  1017.         foreach (string d, integer id, md, {
  1018.         if (id < min)
  1019.         {
  1020.             min = id;
  1021.             device = d;
  1022.         }
  1023.         });
  1024.         if (device != "")
  1025.         {
  1026.         map p_dev = Storage::GetDiskPartition (loader_device);
  1027.         num = myToInteger( p_dev["nr"]:nil );
  1028.         mbr_dev = p_dev["disk"]:"";
  1029.         }
  1030.     }
  1031.     // if bootloader in MBR, activate /boot partition
  1032.     // (partiall fix of #20637)
  1033.     else if (num == 0)
  1034.     {
  1035.         p_dev = Storage::GetDiskPartition (boot_partition);
  1036.         num = myToInteger( p_dev["nr"]:nil );
  1037.         mbr_dev = p_dev["disk"]:"";
  1038.  
  1039.         if (size (Md2Partitions (boot_partition)) > 1)
  1040.         {
  1041.         foreach (string k, integer v, Md2Partitions (boot_partition),{
  1042.             if (search (k, loader_device) == 0)
  1043.             {
  1044.             p_dev = Storage::GetDiskPartition (k);
  1045.             num = myToInteger( p_dev["nr"]:nil );
  1046.             mbr_dev = p_dev["disk"]:"";
  1047.             }
  1048.         });
  1049.         }
  1050.     }
  1051.     if (num != 0)
  1052.     {
  1053.         if (num > 4)
  1054.         {
  1055.         y2milestone ("Bootloader partition type is logical");
  1056.         map tm = Storage::GetTargetMap ();
  1057.         list<map> partitions = tm[mbr_dev, "partitions"]:[];
  1058.         foreach (map p, partitions, ``{
  1059.             if (p["type"]:nil == `extended)
  1060.             {
  1061.             num = p["nr"]:num;
  1062.             y2milestone ("Using extended partition %1 instead",
  1063.             num);
  1064.             }
  1065.         });
  1066.         }
  1067.     }
  1068.     map<string,any> ret = $[
  1069.         "num" : num,
  1070.         "mbr" : mbr_dev,
  1071.         "dev" : Storage::GetDeviceName (mbr_dev, num),
  1072.     ];
  1073.     return ret;
  1074.     }
  1075.  
  1076.     /**
  1077.      * Get a list of partitions to activate if user wants to activate
  1078.      * boot partition
  1079.      * @return a list of partitions to activate
  1080.      */
  1081.     global define list<map<string, any> > getPartitionsToActivate () {
  1082.     map<string,integer> md = $[];
  1083.     if (BootCommon::loader_device == "mbr_md")
  1084.     {
  1085.         md =  Md2Partitions (BootCommon::BootPartitionDevice);
  1086.     }
  1087.     else
  1088.     {
  1089.         md =  Md2Partitions (BootCommon::loader_device);
  1090.     }
  1091.     list<string> partitions = maplist (string k, integer v, md, ``(k));
  1092.     if (size (partitions) == 0)
  1093.     {
  1094.         partitions = [BootCommon::loader_device];
  1095.     }
  1096.     list<map<string,any> > ret = maplist (string partition, partitions, {
  1097.         return getPartitionToActivate (
  1098.         BootCommon::BootPartitionDevice,
  1099.         partition);
  1100.     });
  1101.     return toset (ret);
  1102.     }
  1103.  
  1104.     /**
  1105.      * Get the list of MBR disks that should be rewritten by generic code
  1106.      * if user wants to do so
  1107.      * @return a list of device names to be rewritten
  1108.      */
  1109.     global define list<string> getMbrsToRewrite () {
  1110.     list<string> ret = [BootCommon::mbrDisk];
  1111.     map<string,integer> md = $[];
  1112.     if (BootCommon::loader_device == "mbr_md")
  1113.     {
  1114.         md =  Md2Partitions (BootCommon::BootPartitionDevice);
  1115.     }
  1116.     else
  1117.     {
  1118.         md = Md2Partitions (BootCommon::loader_device);
  1119.     }
  1120.     list<string> mbrs = maplist (string d, integer b, md, {
  1121.         d = getPartitionToActivate (
  1122.         BootCommon::BootPartitionDevice, d
  1123.         )["mbr"]:mbrDisk;
  1124.         return d;
  1125.     });
  1126.     if (contains (mbrs, BootCommon::mbrDisk))
  1127.     {
  1128.         ret = (list<string>)merge (ret, mbrs);
  1129.     }
  1130.     return toset (ret);
  1131.     }
  1132.  
  1133.     /**
  1134.       * Get last change time of file
  1135.       * @param filename string name of file
  1136.       * @return string last change date as YYYY-MM-DD-HH-MM-SS
  1137.       */
  1138.     global define string getFileChangeDate (string filename) ``{
  1139.     map stat = (map) SCR::Read (.target.stat, filename);
  1140.     integer ctime = stat["ctime"]:0;
  1141.     string command = sformat (
  1142.       "date --date='1970-01-01 00:00:00 %1 seconds' +\"%%Y-%%m-%%d-%%H-%%M-%%S\"",
  1143.       ctime);
  1144.     map out = (map) SCR::Execute (.target.bash_output, command);
  1145.     string c_time = out["stdout"]:"";
  1146.     y2debug ("File %1: last change %2", filename, c_time);
  1147.     return c_time;
  1148.     }
  1149.  
  1150.     /**
  1151.       * Save current MBR to /boot/backup_mbr
  1152.       * Also save to /var/lib/YaST2/backup_boot_sectors/%device, if some
  1153.       * existing, rename it
  1154.       * @param device string name of device
  1155.       */
  1156.     global define void saveMBR (string device) ``{
  1157.     string device_file = mergestring (splitstring (device, "/"), "_");
  1158.     string device_file_path = "/var/lib/YaST2/backup_boot_sectors/"
  1159.         + device_file;
  1160.     SCR::Execute (.target.bash,
  1161.         "test -d /var/lib/YaST2/backup_boot_sectors || mkdir /var/lib/YaST2/backup_boot_sectors");
  1162.     if (SCR::Read (.target.size, device_file_path) > 0)
  1163.     {
  1164.         list<string> contents = (list<string>) SCR::Read (.target.dir, "/var/lib/YaST2/backup_boot_sectors");
  1165.         contents = filter (string c, contents, ``(regexpmatch (c, sformat (
  1166.         "%1-.*-.*-.*-.*-.*-.*", device_file))));
  1167.         contents = sort (contents);
  1168.         integer index = 0;
  1169.         integer siz = size (contents);
  1170.         while (index + 10 < siz)
  1171.         {
  1172.         SCR::Execute (.target.remove,
  1173.             sformat ("/var/lib/YaST2/backup_boot_sectors/%1", contents[index]:""));
  1174.         index = index + 1;
  1175.         }
  1176.         string change_date = getFileChangeDate (device_file_path);
  1177.         SCR::Execute (.target.bash, sformat (
  1178.         "/bin/mv %1 %1-%2",
  1179.         device_file_path, change_date));
  1180.     }
  1181.     SCR::Execute (.target.bash, sformat (
  1182.         "/bin/dd if=%1 of=%2 bs=512 count=1 2>&1",
  1183.         device, device_file_path));
  1184.     if (device == mbrDisk)
  1185.     {
  1186.         SCR::Execute (.target.bash, sformat (
  1187.         "/bin/dd if=%1 of=%2 bs=512 count=1 2>&1",
  1188.         device, "/boot/backup_mbr"));
  1189.  
  1190.     }
  1191.     }
  1192.  
  1193.     /**
  1194.       * Update contents of MBR (active partition and booting code)
  1195.       * FIXME move tis function to lilolike.ycp
  1196.       * @return boolean true on success
  1197.       */
  1198.     global define boolean updateMBR () ``{
  1199.           // FIXME: do the real thing in perl_Bootloader
  1200.           if (getLoaderType (false) == "grub") {  
  1201.               activate = ( globals["activate"]:"false" == "true" );
  1202.               repl_mbr = ( globals["generic_mbr"]:"false" == "true" );
  1203.           }
  1204.  
  1205.     y2milestone ("Updating disk system area, activate partition: %1, replace MBR: %2", activate, repl_mbr);
  1206.     if (backup_mbr)
  1207.     {
  1208.         y2milestone ("Doing MBR backup: MBR Disk: %1, loader device: %2",
  1209.         BootCommon::mbrDisk, BootCommon::loader_device);
  1210.         list<string> disks_to_rewrite = (list<string>)toset (merge (
  1211.         getMbrsToRewrite (),
  1212.         [BootCommon::mbrDisk, BootCommon::loader_device]));
  1213.         y2milestone ("Creating backup of boot sectors of %1",
  1214.         disks_to_rewrite);
  1215.         foreach (string d, disks_to_rewrite, {
  1216.         saveMBR (d);
  1217.         });
  1218.     }
  1219.     boolean ret = true;
  1220.     if (repl_mbr && BootCommon::loader_device != mbrDisk)
  1221.     {
  1222.         if (! Stage::initial ())
  1223.         {
  1224.         PackageSystem::Install ("master-boot-code");
  1225.         }
  1226.         y2milestone("Updating code in MBR: MBR Disk: %1, loader device: %2",
  1227.         BootCommon::mbrDisk, BootCommon::loader_device);
  1228.         list<string> disks_to_rewrite = getMbrsToRewrite ();
  1229.         foreach (string d, disks_to_rewrite, {
  1230.         y2milestone ("Copying generic MBR code to %1", d);
  1231.         string command = sformat (
  1232.             "/bin/dd bs=446 count=1 if=%1 of=%2",
  1233.             "/usr/lib/boot/master-boot-code",
  1234.             d);
  1235.         y2milestone ("Running command %1", command);
  1236.         map out = (map)SCR::Execute (.target.bash_output, command);
  1237.         integer exit = out["exit"]:0;
  1238.         y2milestone ("Command output: %1", out);
  1239.         ret = ret && (0 == exit);
  1240.         });
  1241.     }
  1242.  
  1243.     if (activate)
  1244.     {
  1245.       foreach (map m_activate, getPartitionsToActivate (), {
  1246.         any num = m_activate["num"]:0;
  1247.         string mbr_dev = m_activate["mbr"]:"";
  1248.         if (num != 0 && mbr_dev != "")
  1249.         {
  1250.         // if primary partition
  1251.         if ((! is (num, integer)) || num <= 4)
  1252.         {
  1253.             y2milestone ("Activating partition %1 on %2", num, mbr_dev);
  1254.             // FIXME: this is the most rotten code since molded sliced bread
  1255.             // move to bootloader/Core/GRUB.pm or similar
  1256.             // TESTME: make sure that parted does not destroy BSD
  1257.             // slices (#suse24740): cf. section 5.1 of "info parted":
  1258.             //   Parted only supports the BSD disk label system.
  1259.             //   Parted is unlikely to support the partition slice
  1260.             //   system in the future because the semantics are rather
  1261.             //   strange, and don't work like "normal" partition tables
  1262.             //   do.
  1263. //            string command = sformat
  1264. //            ("/usr/sbin/parted -s %1 set %2 boot on", mbr_dev, num);
  1265.             // As a workaround for #167602, moved back to
  1266.             // /sbin/activate, because it does not cause the kernel to
  1267.             // forget about an activated extended partition (it changes
  1268.             // the data on disk without using any ioctl).
  1269.             // FIXME: investigate proper handling of the activate flag
  1270.             // (kernel ioctls in parted etc.) and fix parted
  1271.             string command = sformat
  1272.             ("/usr/sbin/parted -s %1 set %2 boot on", mbr_dev, num);
  1273.             y2milestone ("Running command %1", command);
  1274.             map out = (map)SCR::Execute (.target.bash_output, command);
  1275.             integer exit = out["exit"]:0;
  1276.             y2milestone ("Command output: %1", out);
  1277.             ret = ret && (0 == exit);
  1278.             command = sformat ("/usr/sbin/fix_chs %1 %2",
  1279.             mbr_dev,
  1280.             num);
  1281.             y2milestone ("Running command %1", command);
  1282.             out = (map)SCR::Execute (.target.bash_output, command);
  1283.             exit = out["exit"]:0;
  1284.             y2milestone ("Command output: %1", out);
  1285.             ret = ret && (0 == exit);
  1286.         }
  1287.         }
  1288.         else
  1289.         {
  1290.         y2error ("Cannot activate %1", m_activate);
  1291.         }
  1292.       });
  1293.     }
  1294.     return ret;
  1295.     }
  1296.  
  1297.     /**
  1298.       * Rewrite current MBR with /var/lib/YaST2/backup_boot_sectors/%device
  1299.       * Warning!!! don't use for bootsectors, 446 bytes of sector are written
  1300.       * @param device string device to rewrite MBR to
  1301.       * @return boolean true on success
  1302.       */
  1303.     global define boolean restoreMBR (string device) ``{
  1304.     string device_file = mergestring (splitstring (device, "/"), "_");
  1305.     if (SCR::Read (.target.size, sformat ("/var/lib/YaST2/backup_boot_sectors/%1", device_file))
  1306.         <= 0)
  1307.     {
  1308.         Report::Error ("Can't restore MBR. No saved MBR found");
  1309.         return false;
  1310.     }
  1311.     integer ret = (integer) SCR::Execute (.target.bash, sformat (
  1312.         "/bin/dd of=%1 if=/var/lib/YaST2/backup_boot_sectors/%2 bs=446 count=1",
  1313.         device, device_file));
  1314.     return (ret == 0);
  1315.     }
  1316.  
  1317.     /**
  1318.       * Translate device name to major/minor number
  1319.       * @param device string
  1320.       * @return string major*256+minor hexadecimal without leading 0x, in case
  1321.       *  of any fail return unchanged device node
  1322.       */
  1323.     global define string dev2majmin (string device) ``{
  1324.     map exec_ret = (map) SCR::Execute (.target.bash_output, sformat (
  1325.         "ls -ln %1", device));
  1326.     if (exec_ret["exit"]:1 != 0 && Stage::initial ())
  1327.     {
  1328.         exec_ret = (map) WFM::Execute (.local.bash_output, sformat (
  1329.         "ls -ln %1", device));
  1330.     }
  1331.     if (exec_ret["exit"]:1 != 0)
  1332.         return device;
  1333.         string ls = exec_ret["stdout"]:"";
  1334.         list<string> parts = splitstring (ls, " ");
  1335.         parts = filter (string p, parts, ``(p != ""));
  1336.         string majs = parts[4]:"";
  1337.         majs = substring (majs, 0, size (majs) -1);
  1338.         string mins = parts[5]:"";
  1339.     if (majs == "" || majs == nil || mins == "" || mins == nil)
  1340.         return device;
  1341.         integer maj = tointeger (majs);
  1342.         integer min = tointeger (mins);
  1343.     majs = tohexstring (maj);
  1344.     majs = substring (majs, 2);
  1345.     while (size (majs) < 2)
  1346.         majs = "0" + majs;
  1347.     mins = tohexstring (min);
  1348.     mins = substring (mins, 2);
  1349.     while (size (mins) < 2)
  1350.         mins = "0" +  mins;
  1351.     y2debug ("Translated %1 to %2:%3", device, majs, mins);
  1352.     string ret = sformat ("%1%2", majs, mins);
  1353.         return ret;
  1354.     }
  1355.  
  1356.  
  1357.     /**
  1358.       * Translate device name to major/minor number if the device is not
  1359.       * "usual"
  1360.       * @param device string
  1361.       * @return string the original device parameter if it is "usual",
  1362.       *  same as dev2majmin otherwise
  1363.       */
  1364.     global define string dev2majminIfNonStandard (string device) ``{
  1365.     // don't use major/minor numbers for LVM and MD (seems to be able
  1366.     // to make system unbootable
  1367.     if (Storage::CheckForLvmRootFs() || Storage::CheckForEvmsRootFs ()
  1368.         || Storage::CheckForMdRootFs())
  1369.     {
  1370.         return device;
  1371.     }
  1372.  
  1373.     // FIXME: this is broken, detection by name is deprecated
  1374.     if (regexpmatch (device, "^/dev/hd.+$")
  1375.         || regexpmatch (device, "^/dev/sd.+$")
  1376.         || regexpmatch (device, "^/dev/ataraid/.+$"))
  1377.     {
  1378.         return device;
  1379.     }
  1380.     return dev2majmin (device);
  1381.     }
  1382.  
  1383.     /**
  1384.       * Convert any to boolean
  1385.       * @param v any value
  1386.       * @return boolean retyped value
  1387.       */
  1388.     global define boolean tobool (any v) ``{
  1389.     if (v == true)
  1390.         return true;
  1391.     else
  1392.         return false;
  1393.     }
  1394.  
  1395.     /**
  1396.       * Update kernel parameters if some were added in Kernel module
  1397.       * @param orig original kernel parameters or kernel command line
  1398.       * @return kernel command line or parameters with added new parameters
  1399.       */
  1400.     global define string UpdateKernelParams (string orig) ``{
  1401.     list<string> new = splitstring (Kernel::GetCmdLine (), " ");
  1402.     list<string> old = splitstring (orig, " ");
  1403.     list<string> added = (list<string>)BootCommon::difflist (new,
  1404.          splitstring (BootCommon::kernelCmdLine, " "));
  1405.     added = (list<string>)BootCommon::difflist (added, old);
  1406.     old = (list<string>) merge (old, added);
  1407.     if (Stage::initial ())
  1408.     {// move showopts apic to the end
  1409.         boolean showopts = false;
  1410.         boolean apic = false;
  1411.         if (contains (old, "showopts"))
  1412.         showopts = true;
  1413.         if (contains (old, "apic"))
  1414.         apic = true;
  1415.         old = filter (string o, old, ``(o != "apic" && o != "showopts"));
  1416.         if (showopts)
  1417.         old = add (old, "showopts");
  1418.         if (apic)
  1419.         old = add (old, "apic");
  1420.     }
  1421.     return mergestring (old, " ");
  1422.     }
  1423.  
  1424.  
  1425.  
  1426.     /**
  1427.       * Check whether settings were changed and if yes, ask for exit
  1428.       * without saving
  1429.       * @return true if shall exit
  1430.       */
  1431.     global define boolean confirmAbort () ``{
  1432.     if ((! changed) || confirmAbortPopup ())
  1433.         return true;
  1434.     else
  1435.         return false;
  1436.     }
  1437.  
  1438.     /**
  1439.       * Get map of swap partitions
  1440.       * @return a map where key is partition name and value its size
  1441.       */
  1442.     global define map<string, integer> getSwapPartitions () ``{
  1443.     map<string, map> tm = (map<string,map>)Storage::GetTargetMap ();
  1444.     boolean installation = Mode::installation ();
  1445.     map<string, integer> ret = $[];
  1446.     foreach (string k, map v, tm, ``{
  1447.         integer cyl_size = v["cyl_size"]:0;
  1448.         list<map<string,any> > partitions = v["partitions"]:[];
  1449.         partitions = filter(map<string,any> p, partitions, {
  1450.         return p["mount"]:"" == "swap" && ! p["delete"]:false;
  1451.         });
  1452.         foreach (map<string, any> s, partitions, ``{
  1453.         string dev = (string)(s["device"]:"");
  1454.         ret[dev] = (integer)(s["region", 1]:0) * cyl_size;
  1455.         });
  1456.     });
  1457.     y2milestone ("Available swap partitions: %1", ret);
  1458.     return ret;
  1459.     }
  1460.  
  1461.     /**
  1462.       * Get the name of the largest available swap partition
  1463.       * @return string name of the largest partition
  1464.       */
  1465.     global define string getLargestSwapPartition () ``{
  1466.     map<string, integer> swap_sizes = getSwapPartitions ();
  1467.     list<string> swap_parts = (list<string>)maplist (string name, integer size,
  1468.         swap_sizes, ``(name));
  1469.     swap_parts = sort (string a, string b, swap_parts, ``(
  1470.         swap_sizes[a]:0 > swap_sizes[b]:0
  1471.     ));
  1472.     return swap_parts[0]:"";
  1473.     }
  1474.  
  1475.     /**
  1476.       * Create translated name of a section
  1477.       * @param orig string original section name
  1478.       * @return translated section name
  1479.       */
  1480.     global define string translateSectionTitle (string orig) ``{
  1481.     return GfxMenu::translateSectionTitle(orig,
  1482.                           getLoaderType(false));
  1483.     }
  1484.  
  1485.     /**
  1486.       * Check if device is MBR of a disk
  1487.       * @param device string device to check
  1488.       * @return boolean true if is MBR
  1489.       */
  1490.     global define boolean IsMbr (string device) ``{
  1491.     if (regexpmatch (device, "^\/dev\/[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+$"))
  1492.         return true;
  1493.     if (regexpmatch (device, "^\/dev\/[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+\/.*d[0-9]+$"))
  1494.         return true;
  1495.     return false;
  1496.     }
  1497.  
  1498.     /**
  1499.       * Add '(MBR)' to the disk description if it is a MBR of some partition
  1500.       * @param descr string disk description
  1501.       * @param device string disk device
  1502.       * @return string updated description
  1503.       */
  1504.     global define string AddMbrToDescription (string descr, string device) ``{
  1505.     return IsMbr (device)
  1506.         ? sformat ("%1 (MBR)", descr)
  1507.         : descr;
  1508.     }
  1509.  
  1510.     /**
  1511.      * Update the Kernel::vgaType value to the saved one if not defined
  1512.      */
  1513.     global define void UpdateInstallationKernelParameters () ``{
  1514.     map<string,any> saved_params = $[];
  1515.     if (! Stage::initial ())
  1516.     {
  1517.         saved_params = (map<string,any>)SCR::Read (
  1518.         .target.ycp, "/var/lib/YaST2/bootloader.ycp");
  1519.     }
  1520.     if (Kernel::GetVgaType () == "")
  1521.     {
  1522.         string vgaType = (string)(saved_params["vga"]:"");
  1523.         if (vgaType != nil && vgaType != "")
  1524.         Kernel::SetVgaType (vgaType);
  1525.     }
  1526.     if (! Stage::initial ())
  1527.     {
  1528.         Kernel::SetCmdLine (saved_params["installation_kernel_params"]:"");
  1529.     }
  1530.     else
  1531.     {
  1532.         if (SCR::Read (.etc.install_inf.NoPCMCIA) == "1")
  1533.         {
  1534.         Kernel::SetCmdLine (Kernel::GetCmdLine () + " NOPCMCIA");
  1535.         }
  1536.     }
  1537.     }
  1538.  
  1539.     /**
  1540.      * Get additional kernel parameters
  1541.      * @return additional kernel parameters
  1542.      */
  1543.     global define string GetAdditionalFailsafeParams () ``{
  1544.     if (Stage::initial ())
  1545.     {
  1546.         additional_failsafe_params =
  1547.         SCR::Read (.etc.install_inf.NoPCMCIA) == "1"
  1548.             ? " NOPCMCIA "
  1549.             : "";
  1550.     }
  1551.     else
  1552.     {
  1553.         map<string,any> saved_params = (map<string,any>)SCR::Read (
  1554.         .target.ycp, "/var/lib/YaST2/bootloader.ycp");
  1555.         additional_failsafe_params
  1556.         = saved_params["additional_failsafe_params"]:"";
  1557.     }
  1558.     return additional_failsafe_params;
  1559.     }
  1560.  
  1561. /**
  1562.  * Get additional kernel parameters from control file
  1563.  * @return string additional kernel parameters
  1564.  */
  1565. global string GetAdditionalKernelParams () {
  1566.     return ProductFeatures::GetStringFeature (
  1567.         "globals",
  1568.         "additional_kernel_parameters");
  1569. }
  1570.  
  1571. /**
  1572.  * Get additional kernel parameters splitted to a list
  1573.  * @return a list of additional kernel parameters
  1574.  */
  1575. global list<string> ListAdditionalKernelParams () {
  1576.     return ListKernelParamsInLine (GetAdditionalKernelParams ());
  1577. }
  1578.  
  1579. /**
  1580.  * Update graphical bootloader to contain help text of current language
  1581.  * And make the selected installation language default
  1582.  * @return boolean true on success
  1583.  */
  1584. global define boolean UpdateGfxMenuContents () {
  1585.     return GfxMenu::UpdateGfxMenuContents(getLoaderType(false));
  1586. }
  1587.  
  1588. /**
  1589.  * Transform the selected linux section to section for previous kernel
  1590.  * @param section a list representing the bootloader section
  1591.  * @return a list representing the updated section
  1592.  */
  1593. global map<string,any> Linux2Previous (map<string,any> section) {
  1594.     boolean intern_found = false;
  1595.     section["name"] = translateSectionTitle ("previous");
  1596.     section["original_name"] = "previous";
  1597.     section["kernel"] = regexpsub (
  1598.     section["kernel"]:"",
  1599.     sformat ("^(.*)%1[^ ]*(.*)$", Kernel::GetBinary ()),
  1600.     sformat ("\\1%1.previous\\2", Kernel::GetBinary()));
  1601.     if (haskey (section, "initrd"))
  1602.     {
  1603.     section["initrd"] = regexpsub (
  1604.         section["initrd"]:"",
  1605.         "^(.*)initrd[^ ]*(.*)$",
  1606.         "\\1initrd.previous\\2");
  1607.     }
  1608.     return section;
  1609. }
  1610.  
  1611. /**
  1612.  * Update device name according to changes in kernel (eg. SATA)
  1613.  * @param device string the original device name
  1614.  * @return string updated device
  1615.  */
  1616. global string UpdateDevice (string device) {
  1617.     if (Mode::test ())
  1618.     {
  1619.     map mapping = $[
  1620.         "/dev/hda" : "/dev/sda",
  1621.         "/dev/hdb" : "/dev/sdb",
  1622.     ];
  1623.  
  1624.     map d = Storage::GetDiskPartition( device );
  1625.     if( haskey( mapping, d["disk"]:"" ))
  1626.     {
  1627.         if (d["nr"]:nil == nil || d["nr"]:nil == 0)
  1628.         {
  1629.         device = mapping[d["disk"]:""]:"";
  1630.         }
  1631.         else
  1632.         {
  1633.         device = Storage::GetDeviceName(
  1634.             mapping[d["disk"]:""]:"",
  1635.             d["nr"]:nil);
  1636.         }
  1637.     }
  1638.     }
  1639.     else
  1640.     {
  1641.     list<string> devices = Storage::GetTranslatedDevices (
  1642.         installed_version,
  1643.         update_version,
  1644.         [ device ]);
  1645.     device = devices[0]:device;
  1646.     }
  1647.     return device;
  1648. }
  1649.  
  1650. /**
  1651.  * Check if memtest86 is present
  1652.  * @return boolean true if memtest86 section is to be proposed
  1653.  */
  1654. global boolean MemtestPresent () {
  1655.     return (! contains (removed_sections, "memtest"))
  1656.     && (Mode::test ()
  1657.         || (Mode::normal () && Pkg::IsProvided ("memtest86"))
  1658.         || (! Mode::normal () && Pkg::IsSelected ("memtest86")));
  1659.  
  1660. }
  1661.  
  1662. /**
  1663.  * Check for additional kernels which could go to the proposed settings
  1664.  * @return a list of kernels to propose
  1665.  */
  1666. global list<map<string,string> > CheckAdditionalKernels () {
  1667.     list<string> files = (list<string>)SCR::Read (.target.dir, "/boot");
  1668.     string binary = Kernel::GetBinary ();
  1669.     list<string> kernels = filter (string k, files, {
  1670.     return substring (k, 0, size (binary)) == binary;
  1671.     });
  1672.     kernels = filter (string k, kernels, {
  1673.     return k != ""
  1674.         && k != binary
  1675.         && regexpmatch (k, sformat ("^%1-.+$", binary));
  1676.     });
  1677.     if (contains (kernels, binary))
  1678.     {
  1679.     string defaultv = (string)SCR::Read (.target.symlink,
  1680.         sformat ("/boot/%1", binary));
  1681.     defaultv = ""; // FIXME remove this line
  1682.     kernels = filter (string k, kernels, {
  1683.         return k != defaultv;
  1684.     });
  1685.     }
  1686.     list<map<string,string> > ret = maplist (string k, kernels, {
  1687.     string version = regexpsub (k, sformat ("^%1-(.+)$", binary), "\\1");
  1688.     map<string,string> info = $[
  1689.         "version" : version,
  1690.         "kernel" : sformat ("/boot/%1", k)
  1691.     ];
  1692.     if (contains (files, sformat ("initrd-%1", version)))
  1693.     {
  1694.         info["initrd"] = sformat ("/boot/initrd-%1", version);
  1695.     }
  1696.     return info;
  1697.     });
  1698.  
  1699.     y2milestone ("Additional sectinos to propose: %1", ret);
  1700.     return ret;
  1701. }
  1702.  
  1703. /**
  1704.  * Check if the bootloader can be installed at all with current configuration
  1705.  * @return boolean true if it can
  1706.  */
  1707. global boolean BootloaderInstallable () {
  1708.     if (Mode::config ())
  1709.     return true;
  1710.     if (Arch::i386 () || Arch::x86_64 ())
  1711.     // the only relevant is the partition holding the /boot filesystem
  1712.     {
  1713.     DetectDisks ();
  1714.     y2milestone ("Boot partition device: %1",
  1715.         BootCommon::BootPartitionDevice);
  1716.     map dev = Storage::GetDiskPartition( BootCommon::BootPartitionDevice);
  1717.     y2milestone ("Disk info: %1", dev);
  1718.     // MD, but not mirroring is OK
  1719.     // FIXME: type detection by name deprecated
  1720.     if (dev["disk"]:"" == "/dev/md")
  1721.     {
  1722.         map tm = Storage::GetTargetMap ();
  1723.         map md = tm["/dev/md"]:$[];
  1724.         list<map> parts = md["partitions"]:[];
  1725.         map info = $[];
  1726.         foreach (map p, parts, {
  1727.         if (p["device"]:"" == BootPartitionDevice)
  1728.             info = p;
  1729.         });
  1730.         if (tolower (info["raid_type"]:"") != "raid1")
  1731.         {
  1732.         y2milestone ("Cannot install bootloader on RAID (not mirror)");
  1733.         return false;
  1734.         }
  1735.     }
  1736.     // EVMS
  1737.     // FIXME: type detection by name deprecated
  1738.     else if (search (BootPartitionDevice, "/dev/evms/") == 0)
  1739.     {
  1740.         y2milestone ("Cannot install bootloader on EVMS");
  1741.         return false;
  1742.     }
  1743.     // LVM
  1744.     else if (! is( dev["nr"]:(any)0, integer ))
  1745.     {
  1746.         y2milestone ("Cannot install bootloader on LVM");
  1747.         return false;
  1748.     }
  1749.  
  1750.     return true;
  1751.     }
  1752.     else
  1753.     {
  1754.     return true;
  1755.     }
  1756. }
  1757.  
  1758.  
  1759.  
  1760.  
  1761. } //end of include
  1762.