    /**
 * File:	OSRBoot.ycp
 * Module:	repair
 * Summary:	Bootloader checks
 * Authors:	Johannes Buchhold <jbuch@suse.de>
 *
 * $Id: OSRBoot.ycp 22050 2005-03-01 10:17:25Z jsuchome $
 *
 * Provide osr mode information.
 */

{

    module "OSRBoot";

    import "Storage";
    import "StorageDevices";
    import "Report";
    import "Installation";
    import "Mode";
    import "Popup";
    import "Wizard";

    import "Bootloader";
    import "BootCommon";
    import "BootGRUB";
    import "Initrd";
    import "Kernel";

    import "OSRSystem";
    import "OSRLogFile";
    import "OSRExecute";
    import "OSRPkg";
    import "OSRPopup";
    import "OSRCommon";

    textdomain "repair";

    /**
     * Configuration files needed by every boot loader
     */
    global map needed_config_files = $[
	    "modules"		: "/etc/modprobe.conf",
	    "initrd"		: "/etc/sysconfig/kernel",
	    "bootleader"	: "/etc/sysconfig/bootloader"
    ];

    /**
     * Boot loader package name and required version
     */
    map bootloader_packages = $[
	    "lilo"	: [
		$[
		    "pkgname"		: "lilo",
		    "pkgversion"	: "22.3.2-32"
		],
		$[
		    "pkgname"		: "gfxboot",
		    "pkgversion"	: "1.9-34"
		]
	    ],
	    "grub"	: [
		$[
		    "pkgname"		: "grub",
		    "pkgversion"	: "0.92-69"
		]
	    ],
	    "silo"	: [], //"SILO",
	    "milo"	: [], //"MILO",
	    "aboot"	: [], //"aboot",^
	    "elilo"	: [], //"ELILO",
	    "mips"	: [], //"dvh",
	    "s390"	: [], //"zipl",
	    "ppc"	: []  //"boot loader"
    ];

    // function prototypes
    define boolean check_lilo_config();
    define boolean check_grub_config();
    global define symbol repair_grub_config();

    /**
     * Check and repair define for every support boot loader
     */
    map bootloader_config_check = $[
	    "lilo"	: $[
		"config_check"	: check_lilo_config,
		"config_repair"	: repair_grub_config,
		"config_file"	: "/etc/lilo.conf"
	    ],
	    "grub"	: $[
		"config_check"	: check_grub_config,
		"config_repair"	: repair_grub_config,
		"config_file"	: "/etc/grub.conf",
		"device_map"	: "/boot/grub/device.map",
		"stage1"	: "/boot/grub/stage1",
		"stage2"	: "/boot/grub/stage2",
		"menu"		: "/boot/grub/menu.lst"
	    ],
	    "silo"	: $[], //"SILO",
	    "milo"	: $[], //"MILO",
	    "aboot"	: $[], //"aboot",
	    "elilo"	: $[], //"ELILO",
	    "mips"	: $[], //"dvh",
	    "s390"	: $[], //"zipl",
	    "ppc"	: $[]  //"boot loader"
    ];

    list valid_grub_menu_enties = [];

    list grub_generally = [
			   "bootp"	,
			   "color"	,
			   "device"	,
			   "dhcp"	,
			   "hide"	,
			   "ifconfig"	,
			   "pager"	,
			   "partnew"	,
			   "parttype"	,
			   "password"	,
			   "rarp"	,
			   "serial"	,
			   "setkey"	,
			   "terminal"	,
			   "tftpserver"	,
			   "unhide"	,
			   "gfxmenu"	,
    ];

    list grub_menu_only = [
		      "default"	,
		      "fallback"	,
		      "hiddenmenu"	,
		      "timeout"		,
		      "title"
		      ];

    list grub_menu = [
		      "blocklist"	,
		      "boot"		,
		      "cat"		,
		      "chainloader"	,
		      "cmp"		,
		      "configfile"	,
		      "debug"		,
		      "displayapm"	,
		      "displaymem"	,
		      "embed"		,
		      "find"		,
		      "fstest"		,
		      "geometry"	,
		      "halt"		,
		      "help"		,
		      "impsprobe"	,
		      "initrd"		,
		      "install"		,
		      "ioprobe"		,
		      "kernel"		,
		      "lock"		,
		      "makeactive"	,
		      "map"		,
		      "md5crypt"	,
		      "module"		,
		      "modulenounzip"	,
		      "pause"		,
		      "quit"		,
		      "reboot"		,
		      "read"		,
		      "root"		,
		      "rootnoverify"	,
		      "savedefault"	,
		      "setup"		,
		      "testload"	,
		      "testvbe"		,
		      "uppermem"	,
		      "vbeprobe"
    ];

    /**
     * Default settings for /etc/grub.conf
     */
    map grub_conf_defaults = $[ "discswitch"	: "d",
				"addr"		: "0x8000"];

    /**
     * File for backup bootloader settings
     */
    string config_backup_path		= "/bootloader";
    boolean backup_created		= false;

    /**
     * Error message if the check defines found an error
     */
    string error_message 		= "";

    /**
     * Help text if the check defines found an error
     */
    string help_text     		= "";

    /**
     * The name of the boot loader
     */
    string  bootloader		  	= "";

    /**
     * A list with all invalid configuration files.
     */
    list<string> invalid_config_files 	= [];

    /**
     * A list of all not installed boot loader packages
     */
    global list<string> missing_packages = [];

    /**
     * The root mount point.
     */
    global string  root_mountpoint	 = Installation::destdir;

    /**
     * The root device e.g.: /dev/hda2
     */
    global string  root_device		= "";

    /**
     * The boot device e.g.: /dev/hda1
     */
    global string  boot_device		= "";

    global boolean bootloader_error_found = false;

    global list<string> not_valid_files = [];

    global list current_initrd_modules = [];



    /**
     * Reset module settings.
     **/
    global define void Reset()``{
	not_valid_files 	= [];
	bootloader_error_found 	= false;
	invalid_config_files 	= [];
	boot_device 		= "";
	root_device 		= "";
	bootloader 		= "";
	missing_packages 	= [];
	help_text 		= "";
	error_message 		= "";
	current_initrd_modules  = [];
	//not backup_created
    }

    ////////////////////////////////////////////////////////////////////////////
    // Package data access functions
    ////////////////////////////////////////////////////////////////////////////

    /**
     * The minimal package version of a package.
     */
    define string RequiredPackageVersion(string bootloader, string package )``{
	map package_data = (map)
	    find (map pkgdata, bootloader_packages[bootloader]:[],
		``( pkgdata["pkgname"]:"" == package));

	if (package_data == nil || package_data == $[])
	{
	    y2error("package version not found");
	    return "";
	}
	else
	{
	    return package_data["pkgversion"]:"";
	}
    }

    /**
     * Return a list of strings with all required boot loader packages with the current
     * and the required version.
     */
    global define list<string> PackageVersionStrings(string bootloader, list<string> packages )``{
	list<string> ret = [];
	foreach(string package, packages, ``{
	    string package_version = package;
	    if (!Mode::test ())
	    {
		package_version = package_version + " current version: " +
		    Pkg::PkgVersion(package) + " required version: " +
		    RequiredPackageVersion(bootloader, package);
	    }
	    ret = add(ret, package_version );
	});
	return ret;
    }

    /**
     * Return all needed packages of a boot loader.
     */
    global define list<string> BootloaderPackages(string bootloader )``{
	return maplist(map pkgdata, bootloader_packages[bootloader]:[], ``( pkgdata["pkgname"]:"" ));
    }

    /**
     *  Compares the two specified version numbers. Each version number has to be a string
     *  of the form "21.6-34", "34.4.3", ...
     *
     *  API function.
     *  See the testsuite.
     *
     *  @param string version_1 The first version-number as string.
     *  @param string version_2 The second version-number as string.
     *  @return boolean True if the first version number is newer than the second one.
     *  @example if (!OSRVersionIsHigherOrEqual("21.6", "22.3")) y2error("Something's wrong here.")
     */
    global define boolean VersionIsHigherOrEqual( string version_1, string version_2  ) ``{

      list<string>    v_1   = [];
      list<string>    v_2   = [];
      integer n_1   = 0;
      integer n_2   = 0;
      integer index = 0;
      integer max   = 0;

      // split the version-strings at the dots- and minus-characters "24.5.6" -> ["24", "5", "6"]
      v_1 = splitstring(version_1, ".-");
      v_2 = splitstring(version_2, ".-");

      // select the smaller one of the two numbers
      if (size(v_1) < size(v_2))
      {
	  max = size(v_1);
      }
      else
      {
	  max = size(v_2);
      }

      // iterate the splitted version-numbers
      while(index < max)
      {
	  n_1 = tointeger (v_1[index]:"0");
	  n_2 = tointeger (v_2[index]:"0");

	  if (n_1 > n_2)
	  {
	      return true;
	  }
	  else if (n_1 < n_2)
	  {
	      return false;
	  }
	  index = index + 1;
      }

      // if the splitted numbers were all equal, look if the first version-number
      // consits of more parts than the second one
      return (size(v_1) >= size(v_2));
  };


    /**
     * build the help text with the package summary
     * for all specified packages
     */
    define string build_packages_help(list<string> missing_packages )``{
	string help_text = "";
	foreach(string package, missing_packages,``{
	    help_text = help_text +sformat("<b>%1</b><br>%2<br>",package, Pkg::PkgSummary(package));
	});
	return help_text;
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Checking sysconfig fils and boot loader packages
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Check the main config file for boot loader and kernel initrd
     */
    global define boolean CheckSysconfigFiles(string root)``{

	if( root != "" ) root_mountpoint = root;

	// TODO check if one module is needed at least
	string r1 = (string) SCR::Read (.sysconfig.kernel.INITRD_MODULES);
	if( r1 == nil ) not_valid_files = add(not_valid_files, needed_config_files["initrd"]:"");

	r1 = (string) SCR::Read (.sysconfig.bootloader.LOADER_TYPE);
	if( r1 == nil || r1 == "" ) not_valid_files = add(not_valid_files, needed_config_files["bootloader"]:"");

	integer r2 = Mode::test () ? 0 : (integer) WFM::Execute(.local.bash,
	    sformat("/usr/bin/test -f %1%2",
		root_mountpoint, needed_config_files["modules"]:""));
	if (r2 != 0)
	{
	    not_valid_files = add (not_valid_files,
		needed_config_files["modules"]:"");
	}

	if( size( not_valid_files ) > 0 )
	{
	    bootloader_error_found = true;
	    y2error("checking the sysconfig file found error");
	}
	return size( not_valid_files ) == 0;
    }


    /**
     * Check boot loader packages.
     * @param what = "installed" if all packages are installed
     *	      what = "version"   if all packages have the required version
     *        what = "verify"    if all packages are not damaged
     */
    global define boolean CheckLoaderPackages(string loader, string root, string what )``{

	if( loader 	!= "") bootloader 	= loader;
	if( root 	!= "") root_mountpoint	= root;

	missing_packages = [];

	OSRPkg::OpenPkg( root_mountpoint );

	// check all needed packages
	foreach(string package, BootloaderPackages( bootloader ), ``{

	    if( what == "installed" )
	    {
		if(!Mode::test () && ! Pkg::IsProvided ( package ) ) {
		    missing_packages = add(missing_packages, package);
		}
		else {
		    y2milestone("installed package %1", package );
		}
		// TODO check dependencies
	    }
	    else if ( what == "verify" )
	    {
		//Pkg::XXX not support
		string command = sformat("/bin/rpm -V -r\"%1\" %2", root_mountpoint, package);

		if ( ! OSRExecute::CommandOutput(.local.bash_output, command)) {
		    missing_packages = add(missing_packages, package);
		}
		else {
		    y2milestone("package %1 verified", package);
		}
	    }
	    else if ( what == "version" )
	    {
		if (!Mode::test () &&
		    !VersionIsHigherOrEqual (Pkg::PkgVersion (package),
			RequiredPackageVersion(bootloader, package)))
		{
		    missing_packages = add(missing_packages, package );
		}
		else {
		    y2milestone("package %1 version is ok.", package);
		}
	    }
	    else {
		y2error("CheckLoaderPackages what is not valid");
	    }

	});

	if ( size(missing_packages) > 0 )
	{
	    bootloader_error_found = true;
	    y2error(" package check found error");
	}
	return size(missing_packages) == 0;
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Install missing or damaged boot loader packages
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Install all missing package if the user accept.
     */
    global define symbol InstallLoaderPackage(string loader, string root, string install_reason )``{

	if( loader 	!= "" ) bootloader 	= loader;
	if( root 	!= "" ) root_mountpoint	= root;

	string help_text 	= build_packages_help( missing_packages );
    // error message: %1 is explanation, %2 is list of packages
	string error_message  	= sformat(_("
%1

%2

Press Repair to install these packages.
"),install_reason, mergestring( missing_packages, "\n" ));


	if( OSRPopup::Repair(_("Boot loader package missing."), error_message, help_text)) {

	    OSRPkg::root_mountpoint  = root;
	    OSRPkg::missing_packages = missing_packages;
	    return OSRPkg::InstallMissing(false);
	}
	else
	{
	    y2milestone(" installing missing loader packages was canceled");
	    return `cancel;
	}
    }

    ///////////////////////////////////////////////////////////////////////////
    // Check initrd modules
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Check the specified initrd modules.
     */
    global define boolean CheckInitrdModules(string root )``{
	if( root != "") root_mountpoint = root;

	string crs = (string) SCR::Read (.sysconfig.kernel.INITRD_MODULES);
	current_initrd_modules = splitstring(crs, " ");
	y2milestone("current initrd_modules %1", current_initrd_modules);

	list storage_initrd = Storage::GetRootInitrdModules();
	y2milestone(" found initrd modules %1",storage_initrd );

	//add found modules to Boot::initrdModules
	foreach(string m, (list<string>) storage_initrd, ``{
        Initrd::AddModule (m, "");
	});

	list listed_modules = Initrd::ListModules();
	y2milestone("needed initrd_modules in Boot %1", listed_modules );
	list<string> missing = (list<string>) filter(string s, (list<string>)listed_modules,
	    ``( !contains(current_initrd_modules, s)));

//	if( Mode::test () ) missing = add(missing, "reiserfs");

	if (size( missing ) > 0)
	{
	    help_text = _("
The variable INITRD_MODULES contains the list of modules
to add to the initial RAM disk by calling the script \"mkinitrd\"
Examples are drivers for SCSI controllers and LVM or reiserfs.
");
	    error_message = sformat(_("
The file %1 contains a list
of modules to add to the initial
RAM disk by calling the script \"mkinitrd\".
YaST is missing the following modules:

%2

Press \"Repair\" to add the missing
modules to the initial RAM disk.
"), needed_config_files["initrd"]:"", mergestring( missing , "\n" ));


	    return false;
	}
	return true;
    }


    /**
     * Wirte initrd modules and call mkinitrd
     */
    global define symbol RepairInitrdModules()``{
	if ( ! OSRPopup::Repair(_("Initrd modules missing."), error_message, help_text) ) {
		return `cancel;
	}
	boolean ret           = true;
	string  kernel_initrd = mergestring
	    ((list<string>)Initrd::ListModules(), " ");

	if ( ! Mode::test () )
	{
	    // write INITRD_MODULES - Initrd::Write() could be used?
	    SCR::Write (.sysconfig.kernel.INITRD_MODULES, kernel_initrd);
	    SCR::Write (.sysconfig.kernel, nil);

	    string destproc = "/proc";
	    SCR::Execute (.target.mkdir, destproc, 0755);
	    SCR::Execute (.target.mount, ["proc", destproc], "-t proc");
	    ret = (0 == SCR::Execute(.target.bash, "/sbin/mkinitrd >> /var/log/YaST2/y2logmkinitrd 2>> /var/log/YaST2/y2logmkinitrd"));
	    SCR::Execute (.target.umount, destproc );
	}

	if (! ret)
        {
	    // question in a popup box
            if ( Popup::YesNo( _("Calling mkinitrd failed.
Display the log file?
")))
            {
                Popup::ShowFile( _("The Output of the Script mkinitrd:"), "/var/log/YaST2/y2logmkinitrd" );
            }
        }
	if( ret ) return `ok;
	else return `error;
    }

////////////////////////////////////////////////////////////////////////////
// Check the boot loader configuration files
////////////////////////////////////////////////////////////////////////////

    /**
     * Check if the boot loader conf file exists.
     */
    global define boolean ExistsConf(string loader, string root  )``{
	if ( loader != "" ) bootloader 		= loader;
	if ( root   != "" ) root_mountpoint	= root;

	// look if the file lilo.conf exists an has a positive size
	if ( ! (SCR::Read(.target.size,  bootloader_config_check[bootloader, "config_file"]:"") > 0 ))
	{
	    //No conf file found
	    help_text = sformat(_("
<p>The file %1 was not found.</p>"), bootloader_config_check[bootloader, "config_file"]:"") +

_("<p>The boot loader configuration file contains
defective data. Depending on the boot loader
type, the syntax of the configuration file
differs. </p>
");

	    error_message = _("
The main configuration file of the boot loader was
not found.

If you changed the boot loader configuration
manually and the system starts correctly,
skip this dialog and continue the detection
sequence.
If you never changed anything affecting
the boot loader, press Repair
to create a new configuration file.
");

	    invalid_config_files = add(invalid_config_files,  bootloader_config_check[bootloader, "config_file"]:"");
	    bootloader_error_found = true;
	    y2error("can't find boot loader conf file");
	    return false;
	}
	return true;
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Check the lilo configuration file
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////


    /**
     * Check the lilo conf file.
     */
    define boolean check_lilo_config()``{

	// Be careful: use the mountpoint of the root partition for execution of this command!
	string command = sformat("%1/sbin/lilo -r %1 -t -v", root_mountpoint );

	if(  OSRExecute::Command(.local.bash, command ))
	{
	    return true;
	}
	else {

	    y2error("lilo config file %1 is not valid", bootloader_config_check[bootloader, "config_file" ]:"");

        // help text 1/2
	    help_text = _("
<p>The command /sbin/lilo -t checks the syntax
of the LILO boot loader configuration file.</p>
") +

// help text 2/2
_("<p>This command returned an error. This normally means
your system is not bootable.

It is recommended to create a new configuration
file.</P>
");

        // error message
	    error_message = _("
The syntax of the boot loader configuration
file is not valid.
The configuration file contains defective
data needed for booting the Linux system.

Press \"Repair\" to have YaST generate a new
configuration file.

If you are sure your configuration file
contains no errors, press \"Skip\".
");
	    return false;
	}

    }


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Device check.
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * Returns false if the specified device can not
     * found in the target_map (Storage module)
     */
    define boolean exists_device(string dev_name )``{

	if(dev_name == "" ) return true;

	// if device is a floppy
	if( StorageDevices::FloppyDrives == [] || StorageDevices::FloppyDrives == nil ) StorageDevices::Probe(false);
	y2milestone("FloppyDrives %1", StorageDevices::FloppyDrives );
	map ret =  find(map e, StorageDevices::FloppyDrives, ``(e["dev_orig"]:(e["dev_name"]:"") == dev_name));
	if ( ret != nil && size( ret ) > 0 ) return true;

	// if device is a hard disk
	ret 	= Storage::GetMainDev( dev_name );
	if ( ret != nil && size( ret ) > 0 ) return true;

	// if device is a partition
	map subdevkeys    = Storage::GetDiskPartition( dev_name );
	ret = Storage::GetSubDev( Storage::GetSubDevKey(subdevkeys["disk"]:"", subdevkeys["nr"]:0));
	if ( ret != nil && size( ret ) > 0 ) return true;

	// fix grub bug
	if ( substring( dev_name, 0, 7) == "/dev/fd" )
	{
	    return true;
	}

	// device does not exists
	y2error("device with name %1 not found", dev_name);
	return false;
    }

    /**
     * Convert a grub device (hd0) to a lilo device name (/dev/hda1)
     * grub2Lilo can't convert floppy device.
     */
    define string grubDev2LiloDev(string grub_device )``{

	if ( grub_device == "")
	    return "";

	string ret = "";
	if ( !regexpmatch(grub_device, ".fd.*"))
	{
	    BootGRUB::readDeviceMap();
	    ret = BootGRUB::grubDev2unixDev(grub_device);
	}
	else
	{
	    ret = "/dev/" + substring(grub_device, 1, 3);
	}
	y2milestone("grubDev2unixDev converts %1 to %2", grub_device, ret );
	return ret;
    }

    /**
     * Check if a device (grub syntax) exist.
     */
    define boolean exists_grub_device(string grub_device )``{

	//"device":"(hd0)"
	if( exists_device(grubDev2LiloDev( grub_device )))
	{
	    y2milestone(" device %1 exists", grub_device);
	    return true;
	}
	else
	{
	    y2error(" device %1 not found", grub_device);
	    return false;
	}
    }



    //////////////////////////////////////////////////////////////////
    // grub conf file checks
    //////////////////////////////////////////////////////////////////

    /**
     * Check a grub device entry.
     */
    define boolean check_grub_device(string grub_device )``{
	y2milestone("check grub device: %1", grub_device);
	if ( grub_device == "") return true;
	return exists_grub_device(grub_device);
    }

    /**
     * Check if the root entry exists and is the expected
     */
    define boolean check_grub_root(string root )``{
	y2milestone("check grub root: %1", root);
	if( ! exists_grub_device(root)) return false;

	string check_dev = root_device;

	if( boot_device != root_device )
	{
	    check_dev = boot_device;
	}

	if( check_dev == grubDev2LiloDev(root)) return true;
	else return false;
    }

    /**
     * Check the addr entry of a grub conf file.
     */
    define boolean check_grub_addr( string addr  )``{
	if( addr == "" )
	{
	    addr = grub_conf_defaults["addr"]:"";
	    y2milestone("adrr not set - use default addr");
	}

	if( addr != grub_conf_defaults["addr"]:""){
	    y2error("grub addr (%1) changed not default %2", addr, grub_conf_defaults["addr"]:"");
        // TODO is it a problem, that addr is different from default?
	}
	return true;
    }

    /**
     *
     */
    define boolean check_grub_discswitch (string discswitch)``{

	if (discswitch != "")
	{
	    if (discswitch != grub_conf_defaults["discswitch"]:"")
	    {
		y2error("grub discswitch (%1) changed not default %2",
		    discswitch,  grub_conf_defaults["discswitch"]:"");
	    }
	}
	return true;
    }

    /**
     * Test if stage1 exist.
     */
    define boolean check_grub_stage1( string stage1 )``{
	if ( stage1 == "" )
	{
	    stage1 = bootloader_config_check[bootloader, "stage1"]:"";
	}
// y2internal ("grub root: %1", grubDev2LiloDev(grub_conf["root"]:""));
// TODO: get the mount point of device...
// file can be on other partition! (= grub_conf["root"]) -> hack:
	return OSRExecute::Command(.local.bash, sformat(
        "/usr/bin/test -f %1%2 -o -f %1/boot%2", root_mountpoint, stage1));
    }

    /**
     * Test if stage2 exist.
     */
    define boolean check_grub_stage2(string stage2)``{
	return OSRExecute::Command(.local.bash, sformat(
        "/usr/bin/test -f %1%2 -o -f %1/boot%2", root_mountpoint, stage2 ));
    }

    ///////////////////////////////////////////////////////////////////////////
    // grub menu file checks
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Checks the keys of one map.
     */
    define boolean check_grub_menu_entry_map(map entries)``{
	boolean error_found = false;
	foreach(string key, string value, (map<string,string>)entries ,``{
	    if( ! contains(valid_grub_menu_enties, key ))
	    {
		y2error("key -%1- not found - no valid key", key);
		error_found = true;
	    }
	});
	return ! error_found;
    }

    /**
     * Check the keys of the specified map.
     * @param entries = $["color":"white/blue black/light-gray",
     *                    "default":"0", "gfxmenu":"(hd0,2)/boot/message",
     *                    "timeout":"8"]
     * @return true if all keys are valid
     */
    define boolean check_grub_global_options(map  entries )``{
	valid_grub_menu_enties =  union( grub_generally, grub_menu_only );
	return check_grub_menu_entry_map(entries);
    }

    /**
     * Checks all maps of the specified list.
     * @param entries = [$["initrd":"(hd0,2)/boot/initrd",
     *                   "kernel":"(hd0,2)/boot/vmlinuz root=/dev/hda3   vga=791", "title":"linux"],
     *        error    ->$["asd":"", "chainloader":"+1", "makeactive":"true", "root":"(hd1,0)", "title":"windows"],
     *                   $["chainloader":"+1", "root":"(fd0)", "title":"floppy"],
     *                   $["initrd":"(hd0,2)/boot/initrd.shipped", "kernel":"(hd0,2)/boot/vmlinuz.shipped root=/dev/hda3
     *                      ide=nodma apm=off acpi=off vga=normal nosmp maxcpus=0 disableapic 3", "title":"failsafe"]]
     */
    define boolean check_grub_menu_entries(list entries )``{
	valid_grub_menu_enties = union( grub_menu, grub_menu_only);
	boolean error_found = false;
	foreach(map menu, (list<map<any,any> >)entries, ``{
	    error_found = ! (check_grub_menu_entry_map(menu) == false ) ? false : !error_found;
	});
	return ! error_found;
    }

   /**
     * reads specified section and returns values as list [$[option:$[value:..., comment:...]]]
     * @param type string sections / ""
     * @param section string
     * @return map map as described above
     */
    global define map sectOptions2Map(string loader ,string type, string section) ``{
	path p = .;

	if (type == "")
	{
	    p = topath (loader);
	}
	else
	{
	    p = add( add (topath (loader), "sections"), section);
	}

	list optlist = SCR::Dir(p);

	if (type == "")
	{
	    optlist = filter(string e, (list<string>) optlist, ``(e != "sections"));
	}

	any value   = "";
	map retval  = $[ ];

	foreach (string e, (list<string>) optlist, ``{
	    value = BootCommon::mod2ui (SCR::Read(add(p, e)));
	    if (BootCommon::isSpecial(e))
	    {
		retval = add (retval,
		    e, mergestring ((list<string>)SCR::Read(add(p, e)), ","));
	    }
	    else
	    {
		retval = add(retval, e, sformat("%1", value));
	    }
	});
	return retval;
    }


    /**
     * Check the grub menu and the grub menu entries.
     */
    define boolean check_grub_menu( string menu )``{

	string menu_dev  = "";
	string menu_path = "";

	if( menu == "" )
	{
	    menu_path = bootloader_config_check["grub", "menu"]:"";
	}
	else {
	    menu_dev  = substring(menu, 0, findfirstof(menu, ")") +1 );
	    menu_path = substring(menu, findfirstof(menu, ")") +1 );
	}
	y2milestone("menu_path %1, menu_dev %2, menu: %3",menu_path,menu_dev,menu);

	if( menu_dev != "" && ! exists_grub_device(menu_dev ) )
	{
	    invalid_config_files = add(invalid_config_files, menu_path );
	    y2error("grub menu device not found %1", menu);
	    return false;
	}
	// TODO: get the real mountpoint for boot partition
	if(!OSRExecute::Command(.local.bash, sformat(
        "/usr/bin/test -f %1%2 -o -f %1/boot%2", root_mountpoint, menu_path)))
	{
	    invalid_config_files = add(invalid_config_files, menu_path );
	    y2error("grub menu file not found: %1",menu_path);
	    return false;
	}
	else
	{
	    // read grub menu file
	    map global_options = sectOptions2Map("grub" , "", "");
	    list sects         = SCR::Dir(.grub.sections);
	    list sections      = [];

	    foreach(string e, (list<string>) sects, ``{ sections = add(sections, sectOptions2Map("grub", "sections", e)); });

	    // debug grub menu file
	    y2milestone("options  : %1", global_options );
	    y2milestone("sections : %1", sections       );

	    // check keys in the grub menu file
	    boolean ret = check_grub_global_options(global_options) && check_grub_menu_entries(sections);

	    if( ! ret )
	    {
		y2error("the grub menu file is not valid");
		invalid_config_files = add(invalid_config_files, menu_path );
	    }
	    return ret;
	}
    }


    /**
     * Check the grub device map.
     */
    define boolean check_grub_device_map()``{

	// TODO: get the real mountpoint for boot partition
	string grub_dir = root_mountpoint +  "/boot/grub";
	if (! OSRExecute::Command(.local.bash, sformat("/usr/bin/test -d %1",grub_dir)))
	{
	    y2error("grub directory does not exist");
	    invalid_config_files = add(invalid_config_files, bootloader_config_check[bootloader, "device_map"]:"" );
	    return false;
	}

	string dev_map = (string) SCR::Read(.target.string,
	    bootloader_config_check[bootloader, "device_map"]:"" );

	if( dev_map == "" || dev_map == nil )
	{
	    y2error("reading device map was not successful");
	    invalid_config_files = add(invalid_config_files, bootloader_config_check[bootloader, "device_map"]:"" );
	    return false;
	}

	list maps = filter (string e, splitstring (dev_map, "\n"), ``(e != ""));
	maps      = maplist (string e, (list<string>) maps, ``{
	    return filter (string f, splitstring (e, " \t"), ``(f != ""));
	});

	boolean error_found = false;
	foreach (list e, (list<list<any> >)maps, ``{
	    if ( ! error_found )
	    {
		error_found = ! exists_grub_device( e[0]:"");
		if( error_found )
		    y2error("grub device map: device not found %1", e[0]:"");
	    }
	    if ( ! error_found )
	    {
		error_found = ! exists_device (e[1]:"");
		if ( error_found )
		    y2error("grub device map: device not found %1", e[0]:"");
	    }
	});

	if ( error_found == nil || error_found  )
	{
	    invalid_config_files = add(invalid_config_files,  bootloader_config_check[bootloader, "device_map"]:"" );

	    return false;
	}
	return true;
    }

    /**
     * Check the sysntax of grub configuration files:
     * /boot/grub/device.map
     * /boot/grub/menu.lst
     * /etc/grub.conf
     */
    define boolean check_grub_config() {

	// check /boot/grub/device.map
	// (fd0)   /dev/fd0
	// (hd0)   /dev/hda
	// (hd1)   /dev/hdb
	boolean grub_device_map = check_grub_device_map();
	if (!grub_device_map)
	{
	    y2error("grub devide map contains errors");
	}

	/* systax of grub_conf:
	  $[
	    "addr"		: "0x8000",
	    "device"		: "(hd0)",
	    "discswitch"	: true,
	    "menu"		: "(hd0,2)/boot/grub/menu.lst",
	    "root"		: "(hd0,2)",
	    "s2prefix"		: "--stage2=/boot/grub/stage2",
	    "stage1"		: "/boot/grub/stage1",
	    "stage2"		: "/boot/grub/stage2"]
	*/
	string conf_file_contens = (string) SCR::Read(.target.string,
	    bootloader_config_check[bootloader, "config_file"]:"");

	map grub_conf = BootGRUB::parseGrubConf(conf_file_contens);
        y2debug ("grub_conf: %1", grub_conf);

	boolean embedding	= false;
	if (grub_conf["command"]:"" == "setup")
	{
	    embedding		= true;
	    y2milestone ("embedding stage 1.5 -> reduced checking");
	}

	boolean grub_device	= check_grub_device (grub_conf["device"]:"" );
	boolean grub_root       = check_grub_root (grub_conf["root"]:"" );
	boolean grub_addr	= embedding ||
			    check_grub_addr (grub_conf["addr"]:"" );
	boolean grub_discheck	= embedding ||
			    check_grub_discswitch (grub_conf["discswitch"]:"");
	boolean grub_stage1	= embedding ||
			    check_grub_stage1 (grub_conf["stage1"]:"");
	boolean grub_stage2	= embedding ||
			    check_grub_stage2 (grub_conf["stage2"]:"");
	boolean grub_menu	= embedding ||
			    check_grub_menu (grub_conf["menu"]:"" );

	if ( ! grub_device )
        y2error("grub conf device entry is invalid");
	if ( ! grub_root   )
        y2error("grub conf root entry is invalid");
	if ( ! grub_addr   )
        y2error("grub conf addr entry is invalid");
	if ( ! grub_discheck)
        y2error("grub conf discswitch entry is invalid");
	if ( ! grub_stage1 )
        y2error("grub conf state1 is invalid");
	if ( ! grub_stage2 )
        y2error("grub conf state2 is invalid");

	if ( grub_menu == nil ) {
	    y2error("grub menu is invalid");
	    grub_menu = false;
	}
	if (grub_device && grub_root && grub_addr && grub_menu &&
	    grub_discheck && grub_stage1 && grub_stage2 && grub_device_map)
	{
	    return true;
	}
	else
	{
	    string menu_path = grub_conf["menu"]:"";
	    if (menu_path == "" || menu_path == nil)
	    {
		menu_path = bootloader_config_check[bootloader, "menu"]:"";
	    }
	    else
	    {
		menu_path = substring(menu_path , findfirstof(menu_path, ")") +1 );
	    }

	    // help text 1/4
	    help_text = _("
<p>A default GRUB installation needs three
configuration files:</p>
") +

	    sformat("<p>%1</p>", mergestring ([
		bootloader_config_check [bootloader, "device_map"]:"",
		bootloader_config_check [bootloader, "config_file"]:"",
		menu_path
	    ],"<br>")) +

// help text 2/4
_("<P>YaST found the following files
that are not valid:</p>
") +

	    sformat ("<p>%1</p>", mergestring (invalid_config_files, "<br>")) +

// help text 3/4
_("<p>This generally means that your system is not
bootable.</p>
") +

// help text 4/4
_("<p>Creating a new boot loader
configuration is recommended.</p>
");

	    // error text
	    error_message = _("
The configuration of the boot loader
contains errors.

Press \"Repair\" to have YaST generate a new
configuration.

If you are sure your configuration
contains no errors and your system
is bootable, press \"Skip\".
");

	    return false;
	}
    }

    /**
     * Check the boot loader configuration.
     */
    global define boolean CheckConfig(string loader , string root, string root_dev, string boot_dev )``{

	if ( loader   != "" )  bootloader 	= loader;
	if ( root     != "" )  root_mountpoint	= root;
	if ( root_dev != "" )  root_device	= root_dev;
	if ( boot_dev != "" )  boot_device 	= boot_dev;

	// reset settings
	error_message 		= "";
	help_text 		= "";
	invalid_config_files 	= [];

	if (!haskey (bootloader_config_check [bootloader]:$[], "config_check"))
	{
	    y2error("boot loader config check not supported");
	    bootloader_error_found = true;
	    return false;
	}
	boolean () check_f =
	    bootloader_config_check[bootloader,"config_check"]:OSRCommon::False;
	if (!Mode::test () && !check_f ())
	{
	    bootloader_error_found = true;
	    y2error("boot loader config check returned an error");
	    return false;
	}
	return true;
    }


    /**
     * Repair a damaged grub configuration.
     */
    global define symbol repair_grub_config()``{

	// change root for boot loader module call - why??
	Bootloader::setLoaderType (nil);
	Bootloader::Reset();
	Bootloader::Propose(); // does not read current configuration...
	map retmap = (map)
	    WFM::CallFunction ("bootloader_proposal",["AskUser", $[]]);
	symbol ret = retmap["workflow_sequence"]:`next;

	if( ret == `next )
	{
	    Wizard::CreateDialog ();

	    // prepare environment for saving boot loader settings
	    string destproc = "/proc";
	    SCR::Execute (.target.mkdir, destproc, 0755);
	    SCR::Execute (.target.mount, ["proc", destproc], "-t proc");
	    SCR::Execute (.target.bash, "/sbin/SuSEconfig --module bootsplash");

	    boolean g_ret = Bootloader::Write();
	    Wizard::CloseDialog();

	    if ( g_ret )
		// message popup
	      Report::Message(_("The boot loader was installed successfully."));

	    SCR::Execute (.target.umount, "/proc");
	    WFM::Execute(.local.umount, root_mountpoint + "/proc" );
	}
	return ret;
    }


    /**
     * Repair a damaged boot loader configuration.
     */
    global define symbol RepairConfig(string loader, boolean show_message )``{
	if ( loader != "" ) bootloader = loader;
	if ( show_message  )
	{
	    // popup headline
	    if ( !OSRPopup::Repair (_("Boot loader error detected."),
		    error_message, help_text))
	    {
		return `cancel;
	    }
	}

	// set needed value Boot::initrdModules
	CheckInitrdModules( root_mountpoint );
	if (!haskey (bootloader_config_check [bootloader]:$[], "config_repair"))
	{
	    y2error("boot loader config repair not supported");
	    return `error;
	}
	symbol () repair_f = bootloader_config_check [bootloader, "config_repair"]:OSRCommon::SymbolError;
	return repair_f ();
    }

    /**
     * Install a new boot loader
     */
    global define symbol InstallNewLoader()``{

	error_message = _("
YaST cannot find a valid boot loader
installation. Therefore, installing
a new boot loader is recommended.

If you are sure your system boots
correctly, it is not necessary to
reinstall the boot loader.

Otherwise, press \"Repair\"
to install a new boot loader.
");

	help_text = _("
<P>The boot loader is the first thing
you should see after you turn
on a computer with a Linux installation.
Starting a Linux system without
a boot loader is not possible.</P>
") +

_("<P>If you have installed more than
one operating system on a computer,
the boot loader allows you to select
which system to start.</P>
");
	bootloader = "";
	return RepairConfig("",true);
    }


    /**
     * Repairing configuration files.
     */
    global define symbol RepairSysconfigFiles()``{

	// repairing not possible
	if( contains ( not_valid_files, needed_config_files["modules"]:"") )
	{
	    y2error("repairing %1 is not possible", needed_config_files["modules"]:"");
	    Report::Error(sformat (_("
The file %1 was not found.
YaST cannot recover this
file.
"), needed_config_files["modules"]:""));
	    return `error;
	}

	list<string> t_not_valid_file = filter(string file, not_valid_files, ``( file != needed_config_files["modules"]:"" ));

	error_message = sformat(_("
YaST cannot find the following
configuration files:

%1

Therefore, installing a new
boot loader is recommended.

If you are sure your system boots
correctly, it is not necessary to
reinstall the boot loader.

Otherwise, press \"Repair\"
to install a new boot loader.
"), mergestring(maplist(string file, not_valid_files, ``(file)), "\n"));


	return RepairConfig(bootloader,true);
    }
}//EOF
