/**
 * File:
 *   osr_module_bootloader.ycp
 *
 * Module:
 *   YaST Repair bootloader module.
 *
 * Summary:
 *   YaST Repair. Automatic error detection & repair tool for Linux.
 *
 * Author:
 *   Johannes Buchhold <jbuch@suse.de>
 *
 * $Id: osr_module_bootloader.ycp 22045 2005-03-01 10:13:50Z jsuchome $
 */

{
  textdomain "repair";

  import "Bootloader";
  import "Label";
  import "Mode";
  import "Popup";
  import "Stage";
  import "Storage";

  import "OSR";
  import "OSRCommon";
  import "OSRBoot";
  import "OSRSummary";
  import "OSRStatus";
  import "OSRSystem";
  import "OSRFstab";
  import "OSRFsck";
  import "OSRDirect";


  //////////////////////////////////////////////////////////////////////
  //
  //  DETECTION METHODS
  //
  //////////////////////////////////////////////////////////////////////

  /**
   * Check the base boot loader and kernel configuration files
   */
  define boolean OSRBootloaderSysconfigFiles()``{
      /////////////////////////////////////////////////////////////////////////
      //
      // Check sysconfig files ...
      //
      /////////////////////////////////////////////////////////////////////////

      // change root for SCR
      OSRSystem::SetTargetRoot();

      if( ! OSRBoot::CheckSysconfigFiles( OSRSystem::TargetRoot() ) )
      {
        OSRSummary::DetectError("",
           // summary error text, %1 are file names
           sformat(_("The following configuration files were not found:<br>%1"),
           mergestring( OSRBoot::not_valid_files, "<br>")));

        OSRSummary::SetRepairSummary(OSRBoot::RepairSysconfigFiles(),
                       // summary header
                       _("Installing new boot loader..."),
                       // summary text
                       _("New boot loader installation was successful."),
                       // summary text
                       _("Skipped installing a new boot loader."),
                       // summary text
                       _("New boot loader installation was not successful."));

      }
      else {
        OSRSummary::DetectOK("",
            // summary text, %1 are file names
            sformat(_("Configuration files were found:<br>%1"),
            mergestring ((list<string>) maplist(string k, string file,
                (map<string,string>) OSRBoot::needed_config_files, ``(file)), "<br>")));
        OSRCommon::ProvideBoolean("config_files", true );
      }
      return true;
  }

  /**
   * Find the type of the boot loader e.g.: lilo, grub
   */
  define boolean OSRBootloaderType() ``{
      /////////////////////////////////////////////////////////////////////////
      //
      // Searching type of the boot loader type ...
      //
      /////////////////////////////////////////////////////////////////////////
      string loader_type = Bootloader::getLoaderType();
      if ( loader_type != "" && loader_type != nil )
      {
        OSRSummary::DetectOK("",
            // summary text, %1 is boot loader type
            sformat(_("Found %1 boot loader"), loader_type ));
        OSRCommon::ProvideString("bootloader_type", loader_type);
      }
      else {
        // summary error text
        OSRSummary::DetectError("", _("No boot loader was found"));
        OSRSummary::SetRepairSummary(OSRBoot::InstallNewLoader(),
                        // summary header
                       _("Installing new boot loader..."),
                        // summary text
                       _("New boot loader installation was successful."),
                        // summary text
                       _("Skipped installing a new boot loader."),
                        // summary text
                       _("New boot loader installation was not successful."));

      }
      return true;
  }


  /**
   * Find the root and boot partition.
   */
  define boolean OSRBootloaderBootRoot()``{
      /////////////////////////////////////////////////////////////////////////
      //
      // Searching for root and boot partition  ...
      //
      /////////////////////////////////////////////////////////////////////////
      list<string> not_found    = [];

      // Set boot and root device
      OSRBoot::root_device = OSRCommon::RequireString("root_partition");
      OSRBoot::boot_device = OSRFstab::BootDev();

      //if not boot device is specified use root device
      if( OSRBoot::boot_device == "" )
      {
      OSRBoot::boot_device = OSRBoot::root_device;
      }

      //Change root back to / for Boot::Set()
      OSRSystem::SetOrgRoot();
      if (!Mode::test ())
	Bootloader::Propose();
      OSRSystem::SetTargetRoot();

      string root_device = Bootloader::getRootDevice();
      string boot_device = Bootloader::getBootDevice();
      y2milestone("Boot::Set() new root %1 and boot %2 device", root_device,
         boot_device);
      if( boot_device != OSRBoot::boot_device )
      {
        y2error(" boot_device and Bootloader::getBootDevice differ");
      }
      if( root_device != OSRBoot::root_device )
      {
        y2error(" root_device and Bootloader::getRootDevice differ");
      }


      // summary header
      string summary_header = _("Searching for root device...");
      if( OSRBoot::root_device != "" && OSRBoot::root_device != nil )
      {
        OSRSummary::DetectOK(summary_header,
            // summary text
            sformat(_("Root device was found: %1"), OSRBoot::root_device ));
        OSRCommon::ProvideString("root_device", OSRBoot::root_device );
      }
      else {
        // summary text
        OSRSummary::DetectError(summary_header, _("No root device was found"));
        not_found = add( not_found, "root");
      }

      // summary header
      summary_header = _("Searching for boot device...");
      if( OSRBoot::boot_device != "" && OSRBoot::boot_device != nil )
      {
        OSRSummary::DetectOK(summary_header,
            // summary text
            sformat(_("Boot device was found: %1"), OSRBoot::boot_device ));
        OSRCommon::ProvideString("boot_device", OSRBoot::boot_device );
      }
      else {
        // summary text
        OSRSummary::DetectError(summary_header, _("No boot device was found"));
        not_found = add( not_found, "boot" );
      }

      // Root or boot not found
      // OSRStatus::error
      if( size ( not_found ) > 0 )
      {
        // popup header
        string header = _("No valid root partition was found");
        // popup text
        string text =  _("
YaST could not detect any valid root partitions
in your system. Probably, there is no valid
Linux system on this computer. If you are
sure you installed a Linux system, press
\"Retry Scanning\". Otherwise, skip scanning
the system and start a new installation.
");

        if (not_found[0]:"" == "boot")
        {
            // popup header
            header =  _("No Valid Boot Partition Found");
            // popup text
            text =  _("
YaST could not detect any valid boot partitions
in your system. Probably, there is no valid
Linux system on this computer. If you are
sure you installed a Linux system, press
\"Retry Scanning\". Otherwise, skip scanning
the system and start a new installation.
");
        }
        if( Popup::AnyQuestion(header, text,
            // button label
            _("&Retry Scanning"), Label::CancelButton(), `focus_yes ) )
        {
            OSRStatus::RestartScan("find_harddisks");
            return true;
        }
        else
        {
            OSRStatus::ErrorSeverityBlocking();
            if (Stage::initial ())
            {
                OSR::SetReboot();
                y2milestone ( "Make a hard reboot" );
            }
            return false;
        }
     }
      if ( OSRCommon::RequireBoolean("mounted_all") != true )
      {
      if( ! OSRFstab::ReadedSuccessfully())
          OSRCommon::ProvideList("just_umounted", OSRFstab::UmountAllFrom( OSRSystem::TargetRoot()));

      list<map> mounted      = OSRFstab::MountAll( OSRSystem::TargetRoot());
      list<map> success      = filter(map mpe , mounted, ``(mpe["status"]:false == true));

      OSRCommon::ProvideBoolean("mounted_all", true );
      OSRCommon::ProvideList("just_mounted", union( OSRCommon::RequireList("just_mounted"), success));
      }

      return true;
  }

  /**
   * Check if all needed boot loader packages are installed.
   */
  define boolean OSRBootloaderPackagesInstalled()``{
      /////////////////////////////////////////////////////////////////////////
      //
      // Check boot loader packages
      //
      /////////////////////////////////////////////////////////////////////////
      string bootloader                 = OSRCommon::RequireString("bootloader_type");

      // usr "" for root_mountpoint !! root_mountpoint set by OSRBoot::CheckSysconfigFiles..
      if( OSRBoot::CheckLoaderPackages(bootloader, "", "installed" ))
      {
        OSRSummary::DetectOK("",
            // summary text, %1 are packages
            sformat(_("Packages needed by the boot loader were found:<br>%1"),
            mergestring( OSRBoot::BootloaderPackages( bootloader ), "<br>")));

      OSRCommon::ProvideBoolean("bootloader_packages_installed", true);
      }
      else if( size( OSRBoot::missing_packages ) != 0 )
      {
        OSRSummary::DetectError("",
            // summary (error) text, %1 are packages
            sformat(_("Missing packages needed by the boot loader:<br>%1"),
            mergestring(OSRBoot::missing_packages, "<br>")));

        OSRSummary::SetRepairSummary(
            // popup text
            OSRBoot::InstallLoaderPackage(bootloader,"", _("
The following packages needed by the
boot loader are not installed in the
system.
")),
                        // summary header
                        _("Installing packages..."),
                        // summary text
                        _("Package installation was successful."),
                        // summary text
                        _("Missing packages were not installed."),
                        // summary text
                        _("Package installation was not successful."));
      }
      else
      {
        // summary (error) text
        OSRSummary::DetectError("", _("Cannot initialize package database."));

        // popup header
        if( Popup::AnyQuestion(_("YaST cannot initialize the package database."),
            // popup text
            _("
YaST could not initialize the package database.
Probably, there is no valid package database
on this computer.
To check the package database,
press \"Retry Scanning\".
Otherwise press \"Cancel\" and continue with other tests.
"),
            // button label
            _("&Retry Scanning"), Label::CancelButton(), `focus_yes) )
        {
          OSRStatus::RestartScan("check_package_database");
          return true;
        }
        OSRStatus::RepairOmit();
      }
      return true;
  }


  /**
   * Verifying installed boot loader packages
   */
  define boolean OSRBootloaderPackagesVerified()``{

      string bootloader = OSRCommon::RequireString("bootloader_type");
      if( OSRBoot::CheckLoaderPackages(bootloader, "", "verify" ) )
      {

        OSRSummary::DetectOK("", sformat(
// summary text, %1 are packages
_("Packages needed by the boot loader were <br>checked successfully:<br>%1"),
            mergestring(OSRBoot::BootloaderPackages(bootloader), "<br>" )));

        OSRCommon::ProvideBoolean("bootloader_packages_vertified", true);
      }
      else
      {
        OSRSummary::DetectError("", sformat(
// summary text (error), %1 are packages
_("Packages needed by the boot loader were not checked successfully:<br>%1"),
            mergestring(OSRBoot::missing_packages, "<br>")));

        OSRSummary::SetRepairSummary(
            // popup text
            OSRBoot::InstallLoaderPackage(bootloader, "", _("
The state of the following packages
needed by the boot loader is not
valid. Press \"Repair\" to update
these packages.
")),
                        // summary header
                        _("Updating packages..."),
                        // summary text
                        _("Successfully updated packages."),
                        // summary text
                        _("Packages were not updated."),
                        // summary text
                        _("Package update was not successful."));
      }
      return true;
  }

  /**
   * Check the boot loader version.
   */
  define boolean OSRBootloaderPackagesVersion()``{

      string bootloader = OSRCommon::RequireString("bootloader_type");
      if( OSRBoot::CheckLoaderPackages(bootloader,"", "version" ) )
      {
        // summary text
        OSRSummary::DetectOK("", sformat(_("Package versions are ok:<br>%1"),
            mergestring(OSRBoot::PackageVersionStrings(bootloader,
                OSRBoot::BootloaderPackages(bootloader)), "<br>")));

	OSRCommon::ProvideBoolean("bootloader_packages_version", true);
      }
      else {
        OSRSummary::DetectError("",
            // summary text
            sformat(_("Package versions are obsolete:<br>%1"),
            mergestring(OSRBoot::PackageVersionStrings(bootloader,
                OSRBoot::missing_packages), "<br>")));

        OSRSummary::SetRepairSummary(
            // popup text
            OSRBoot::InstallLoaderPackage(bootloader, "", _("
The version of the following packages
is obsolete. Press \"Repair\" to update
these packages.
")),
                        // summary header
                        _("Updating packages..."),
                        // summary text
                        _("Successfully updated packages."),
                        // summary text
                        _("Packages were not updated."),
                        // summary text
                        _("Package update was not successful."));
      }
      return true;
  }


  define boolean OSRBootloaderCheckInitrd() ``{

      if( OSRBoot::CheckInitrdModules("") )
      {
        // summary text
        OSRSummary::DetectOK("", sformat(_("All needed initrd modules found")));
        OSRCommon::ProvideBoolean("bootloader_initrd_valid", true);
      }
      else
      {
        OSRSummary::SetRepairSummary(OSRBoot::RepairInitrdModules(),
                        // summary header
                        _("Repairing missing initrd modules..."),
                        // summary text
                        _("Repair was successful."),
                        // summary text
                        _("Repair was skipped."),
                        // summary text
                        _("Repair was not successful."));
      }
      return true;
  }


  /**
   *  Checks if the boot loader configuration file exists at
   *  the standard location
   */
  define boolean OSRBootloaderConfExists() ``{

      string  bootloader    = OSRCommon::RequireString("bootloader_type");

      if ( OSRBoot::ExistsConf(bootloader, OSRSystem::TargetRoot() ))
      {
        // summary text
        OSRSummary::DetectOK("",_("File exists."));
        OSRCommon::ProvideBoolean("bootloader_conf_exists", true);
      }
      else
      {
        OSRSummary::DetectError("",
            // summary text
            _("Configuration file was not found or is empty."));

        OSRSummary::SetRepairSummary(OSRBoot::RepairConfig(bootloader,true),
                        // summary header
                        _("Creating new boot loader configuration file..."),
                        // summary text
                        _("Creation was not successful."),
                        // summary text
                        _("Creation was skipped"),
                        // summary text
                        _("Creation was not successful."));
      }
      return true;
  };


  /**
   * Check boot loader conf file,
   * e.g. /etc/lilo.conf or ( /etc/grub.conf && /boot/grub/menu.lst )
   */
  define boolean OSRBootloaderCheckConf()``{

      string  bootloader    = OSRCommon::RequireString("bootloader_type");

      if( OSRBoot::CheckConfig( bootloader, OSRSystem::TargetRoot(),
                OSRCommon::RequireString("root_device"),
                OSRCommon::RequireString("boot_device") ))
      {
        OSRSummary::DetectOK("",
            // summary text
            _("Boot loader configuration file check was successful."));
        OSRCommon::ProvideBoolean("bootloader_conf_check", true);
      }
      else
      {
        OSRSummary::DetectError("",
            // summary text
            _("Boot loader configuration file contains errors."));
        OSRSummary::SetRepairSummary(OSRBoot::RepairConfig(bootloader,true),
                        // summary header
                        _("Repairing boot loader configuration..."),
                        // summary text
                        _("Repair was successful."),
                        // summary text
                        _("Repair was skipped."),
                        // summary text
                        _("Repair was not successful."));
      }
      return true;
  }


  /**
   *
   */
  define boolean OSRBootloaderAskUser()``{
      string bootloader     = OSRCommon::RequireString("bootloader_type");
      // summary header
      string summary_header =_("Ask User Concerning Boot Loader Configuration");

      // don't start OSRBootloaderAskUser twice
      if( OSRCommon::RequireBoolean("bootloader_ask_user")) return true;

      y2milestone("ask the user! error was found before %1",
        OSRBoot::bootloader_error_found );
      //if no error was found ask the user
      if( ! OSRBoot::bootloader_error_found )
      {
        // popup header
        if ( Popup::AnyQuestion(_("YaST Boot Loader Check"),
            // popup text
            _("
YaST has checked the boot loader settings
of the system and found no error. 

It is sometimes possible that your
boot loader is damaged anyway. The only way to
detect such an exceptional case is to ask
the user.

If you shut down your system and it start
correctly afterwards, you have a valid
boot loader installation. In this case, you 
should press \"Continue\".
If your system does not start correctly,
press \"Repair\".
"),
            // button label
            _("&Repair"),
            Label::ContinueButton(),
            `focus_no
            ))
      {
          // summary text
          OSRSummary::DetectError(summary_header, _("User Found Error"));
          OSRSummary::SetRepairSummary(OSRBoot::RepairConfig(bootloader,false),
                        // summary header
                        _("Repairing boot loader configuration..."),
                        // summary text
                        _("Repair was successful."),
                        // summary text
                        _("Repair was skipped."),
                        // summary text
                        _("Repair was not successful."));
      }
      else {
          // summary text
          OSRSummary::DetectOK(summary_header, _("User Found No Error"));
      }
      }
      OSRCommon::ProvideBoolean("bootloader_ask_user", true);
      return true;
  }

  /**
   *  Reset boot loader temporary settings.
   */
  define boolean OSRBootloaderReset() ``{

      OSRSystem::SetOrgRoot();
      OSRBoot::Reset();
      return true;
  }

  define symbol OSRDirectBootloader()``{
      y2milestone("Direct start boot loader");

      OSRBoot::root_device = OSRDirect::mounted_root_partition;
      OSRBoot::boot_device = OSRFstab::BootDev();

      y2milestone("root_device_1 %1", OSRBoot::root_device);
      y2milestone("boot_device_1 %1", OSRBoot::boot_device);
      // patch storage entries for Boot::Set!!
      map subdevkeys    = Storage::GetDiskPartition(OSRBoot::root_device );
      any subdevindex	= subdevkeys["nr"]:nil;
      if (subdevindex == nil) subdevindex = "";
      string subdevkey  = Storage::GetSubDevKey(subdevkeys["disk"]:"", subdevindex);

      map part = Storage::GetSubDev( subdevkey );
      part["mount"] = "/";
      Storage::SetSubDev(subdevkey, part );

      //if not boot device is specified use root device
      if( OSRBoot::boot_device == "" )
      {
      OSRBoot::boot_device = OSRBoot::root_device;
      }
      else {
      subdevkeys = Storage::GetDiskPartition(OSRBoot::boot_device );
      subdevkey  = Storage::GetSubDevKey(subdevkeys["disk"]:"", subdevkeys["nr"]:"");

      part = Storage::GetSubDev( subdevkey );
      part["mount"] = "/boot";
      Storage::SetSubDev(subdevkey, part );
      }

      y2milestone("root_device_2 %1", OSRBoot::root_device);
      y2milestone("boot_device_2 %1", OSRBoot::boot_device);

      Bootloader::Propose();

      // compare expected with actual boot, root device
      if(  Bootloader::getBootDevice() != OSRBoot::boot_device )
      {
        y2error(" boot_device and Bootloader::getBootDevice differ");
        Bootloader::setBootDevice (OSRBoot::boot_device);
      }
      if( Bootloader::getRootDevice() != OSRBoot::root_device )
      {
        y2error(" root_device and Bootloader::getRootDevice differ");
        Bootloader::setRootDevice (OSRBoot::root_device);
      }

      OSRBoot::root_mountpoint = OSRSystem::TargetRoot();

      OSRBoot::CheckInitrdModules( OSRSystem::TargetRoot());

      UI::NormalCursor();
      OSRBoot::repair_grub_config();

      OSRFstab::Reset();
      OSRFsck::Reset();
      OSRBoot::Reset();

      return `next;
  }

  //////////////////////////////////////////////////////////////////////
  //
  //  METHODS
  //
  //////////////////////////////////////////////////////////////////////

  /**
   *  Initialization of the module map that contains all important information
   *  for this module.
   *
   *  @return map The map that contains all information about the module osr_module_init.
   */
  define map OSRBootloaderInit() ``{

      y2milestone ("OSRBootloaderInit");

      return  $[
          "name"              :  "osr_module_bootloader",
                                 // module headline
          "headline"          :  _("Boot Loader"),

          "global_entries"    : $[
                      "boot_check" :
                                  // module action label
                      $[ "text" : _("Check Boot Loader Configuration"),
                                  // bootloader module helptext
                         "help" : _("
<p><B>What is a boot loader?</B><BR>
The boot loader is the first thing
you should see after you turned
on a computer with a Linux installion.
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>
") +

_("<P><B>Check Boot Loader Configuration</B><BR>
YaST tries to detect the installed boot
loader. Afterwards, the configuration
files of the found boot loader will be
checked. If no boot loader or an error
in the configuration is detected, you
have the possibility to install a new
boot loader. 
</P>
"),
                         "requires" : [ "fstab_check" ]
                      ]
          ],
          "static_provides" : $[],
          "reset_methods"      : [
                                      // module reset label
                      $["summary"   : _("Reset Boot Loader Settings"),
                        "method"    : OSRBootloaderReset,
                        "provides"  : 10
                      ]
          ],
          // the sequence of the detection methods of this module
          "detect_methods"    :  [
                      $[ "name"   : "find_config_files",
                                      // module method progress label
                         "summary"  : _("Searching configuration files..."),
                         "method"   : OSRBootloaderSysconfigFiles,
                         "requires" :  ["valid_target", "root_mounted" ],
                         "provides" :  ["config_files"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                      $[ "name"   : "find_bootloader_type",
                                      // module method progress label
                         "summary"  : _("Searching for boot loader..."),
                         "method"   : OSRBootloaderType,
                         "requires" :  ["config_files"],
                         "provides" :  ["bootloader_type"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                     $[ "name"    : "find_boot_root",
                         "summary"  : "",
                         "method"   : OSRBootloaderBootRoot,
                         "requires" :  ["bootloader_type"],
                         "provides" :  ["boot_device", "root_device"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                      $[ "name"   : "bootloader_package_installed",
                                      // module method progress label
                         "summary"  : _("Searching for packages"),
                         "method"   : OSRBootloaderPackagesInstalled,
                         "requires" :  ["bootloader_type", "boot_device", "root_device"],
                         "provides" :  ["bootloader_packages_installed"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                      $[ "name" : "verify_bootloader_package",
                                      // module method progress label
                         "summary"  : _("Verifying packages..."),
                         "method"   : OSRBootloaderPackagesVerified,
                         "requires" :  ["bootloader_packages_installed"],
                         "provides" :  ["bootloader_packages_vertified"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                      $[ "name" : "bootloader_package_version",
                                      // module method progress label
                         "summary"  : _("Checking versions..."),
                         "method"   : OSRBootloaderPackagesVersion,
                         "requires" :  ["bootloader_packages_vertified"],
                         "provides" :  ["bootloader_packages_version"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                       $[ "name"    : "bootloader_initrd_modules",
                                      // module method progress label
                         "summary"  : _("Checking initrd modules..."),
                         "method"   : OSRBootloaderCheckInitrd,
                         "requires" :  ["bootloader_packages_version"],
                         "provides" :  ["bootloader_initrd_valid"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                      $[ "name" : "bootloader_conf_exists",
                                      // module method progress label
                         "summary"  : _("Searching boot loader configuration file..."),
                         "method"   : OSRBootloaderConfExists,
                         "requires" :  ["bootloader_initrd_valid"],
                         "provides" :  ["bootloader_conf_exists"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                      $[ "name" : "bootloader_conf_check",
                                      // module method progress label
                         "summary"  : _("Checking boot loader configuration file..."),
                         "method"   : OSRBootloaderCheckConf,
                         "requires" :  ["bootloader_conf_exists"],
                         "provides" :  ["bootloader_conf_check"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ],
                      $[ "name" : "bootloader_ask_user",
                         "summary"  : "",
                         "method"   : OSRBootloaderAskUser,
                         "requires" :  ["bootloader_conf_check"],
                         "provides" :  ["bootloader_ask_user"],
                         "group"    : "boot_check",
                         "progress" : 10
                      ]
          ],
          "direct_methods"  : [
                       $[
                         "name"     : "direct_methods_booloader",
                                          // module method action label
                         "button_text"  : _("Install New Boot Loader"),
                         "method"       : OSRDirectBootloader,
                         "initial_only" : false,
                         "description"  : _("
If you do not see a boot menu after
turning on your computer or after
restarting, press this button to
install a new boot loader.
"),
                         "visible"      : true,
                         "initial_root" : true
                       ]
          ]
      ];
  }

}//EOF
