ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/BootPOWERLILO.ycp

/**
 * File:
 *      modules/BootPOWERLILO.ycp
 *
 * Module:
 *      Bootloader installation and configuration
 *
 * Summary:
 *      Module containing specific functions for POWERLILO configuration
 *      and installation
 *
 * Authors:
 *      Jiri Srain <jsrain@suse.cz>
 *      Joachim Plack <jplack@suse.de>
 *      Olaf Dabrunz <od@suse.de>
 *      Philipp Thomas <pth@suse.de>
 *
 * $Id: BootPOWERLILO.ycp 59966 2009-12-04 15:16:44Z juhliarik $
 *
 */

{
module "BootPOWERLILO";

textdomain "bootloader";

import "Arch";
import "BootArch";
import "BootCommon";
import "BootStorage";
import "Installation";
import "Kernel";
import "Mode";
import "Pkg";
import "Stage";
import "Storage";

/*
 * include ppc specific help messages
 */
include "bootloader/ppc/helps.ycp";

/*
 * read my dialogs
 */
include "bootloader/routines/popups.ycp"; // define confirmSectionDeletePopup




/*
 * This whole code is a big mess. To have a solution at all I included and
 * adapted copies of the code from the old BootPPC.ycp, from common code in
 * lilolike.ycp and others.
 *
 * od - February and March 2006
 */

// partition number for the bootloader (either 41PReP boot or Apple_HFS)
// start with disabled value and let the partition scanner find a match.
global string prep_boot_partition = "";

// map available of 41 PReP partitions, used on iSeries and CHRP
global list<string> prep_boot_partitions = [];

// map available HFS partitions, used on PMac
global list<string> pmac_boot_partitions = [];

// PReP boot partitions that were proposed by partitioner to install BL
global list<string> install_prep_boot_partitions = [];

// saved ID of the base installation source
global integer base_source = -1;

// iSeries specific global settings

// current board attribs
global boolean prep_only_active = true;
global boolean prep_only_iseries_vd = true;
global boolean prep_only_primary = true;
global boolean prep_same_disk_as_root = true;
global list table_items = [];
global string boot_device = "";

string board_type = nil;


global define string getBoardType ();
global define void currentBoardInit ();


include "bootloader/ppc/dialogs.ycp";


/**
 * Update list of 41 PReP boot partitions
 * @return boolean true if list changed, false otherwise
 */
boolean updatePrepBootPartitions () {
    y2milestone ("Detecting PReP partitions: prep_only_active: %1, prep_only_iseries_vd: %2, prep_only_primary: %3", prep_only_active, prep_only_iseries_vd, prep_only_primary);
    map<string,map> targetMap = (map<string,map>)Storage::GetTargetMap ();
    y2milestone ("TargetMap: %1", targetMap);
    list old_prep_boot_partitions = prep_boot_partitions;
    list old_install_prep_boot_partitions = install_prep_boot_partitions;
    prep_boot_partitions = [];
    install_prep_boot_partitions = [];
    y2milestone("old prep_boot_partitions %1", old_prep_boot_partitions);

    foreach (string dname, map ddata, targetMap, ``{
        list<map> partitions = ddata["partitions"]:[];
	y2milestone ("Partitions: %1", partitions);
	partitions = filter (map p, partitions,
	    ``((! p["delete"]:false) && is (p["fsid"]:nil, integer)
		// both partition types 0x41 and FAT16 can be handled by PPC lilo
		&& (p["fsid"]:nil == 65 ||  p["fsid"]:nil == 6)
		&& !contains( [`lvm, `evms, `sw_raid], p["type"]:`primary )
	       ));
	y2milestone ("Filtered existing partitions: %1", partitions);

	// prep_only_iseries_vd means: use only partitions on /dev/iseries/vd*
	if (prep_only_iseries_vd) {
	    partitions = filter (map p, partitions, ``(
		regexpmatch (p["device"]:"", "\/dev\/iseries\/vd.*")
	    ));
	}
	if (prep_only_primary) {
	    partitions = filter (map p, partitions, ``(
		p["type"]:`primary == `primary
	    ));
	}
	y2milestone ("Finally filtered partitions: %1", partitions);
	prep_boot_partitions = (list<string>)
	    merge (prep_boot_partitions,
		   (list<string>)maplist (map p, partitions, ``(p["device"]:""))
	    );
	partitions = filter (map p, partitions, ``(p["prep_install"]:false));
	y2milestone ("Finally filtered recommended partitions: %1",
		     partitions);
	install_prep_boot_partitions = (list<string>)
	    merge (
		   install_prep_boot_partitions,
		   (list<string>) maplist(map p, partitions, ``(p["device"]:""))
	    );
    });
    prep_boot_partitions = filter (string p, prep_boot_partitions, ``(p != ""));
    prep_boot_partitions = sort (prep_boot_partitions);
    y2milestone ("Detected PReP partitions: %1", prep_boot_partitions);
    y2milestone ("Proposed PReP partitions: %1",
		 install_prep_boot_partitions);

    if (old_prep_boot_partitions == prep_boot_partitions
	&& old_install_prep_boot_partitions == install_prep_boot_partitions)
    {
        y2milestone ("PReP Partitions unchanged");
	return false;
    }
    else {
        y2milestone ("PReP Partitions changed");
	return true;
    }
}

/**
 * Select PReP boot partition to propose
 * Changes internal variables.
 */
void choosePrepBootPartition () {
    y2milestone ("Resetting selected PReP boot partition");
    list<string> root_disks = [];
    if (Storage::CheckForLvmRootFs ()) {
        map<string,map> tm = Storage::GetTargetMap ();
	string vg = "";
	foreach (string dev, map info, tm, {
	    if (info["type"]:`CT_UNKNOWN==`CT_LVM) {
	        list<map> volumes = info["partitions"]:[];
		foreach (map v, volumes, {
		    if (v["mount"]:"" == "/") {
			vg = info["name"]:"";
			y2milestone ("Volume group of root FS: %1", vg);
		    }
		});
	    }
	});
	foreach (string dev, map info, tm, {
	    list<map> partitions = info["partitions"]:[];
	    foreach (map p, partitions, {
	        if (p["used_by_device"]:"" == "/dev/" + vg) {
		    root_disks = add (root_disks, dev);
		}
	    });
	});
	y2milestone ("Disks holding LVM with root fs: %1", root_disks);
    }
    else {
        root_disks = [
	    Storage::GetDiskPartition(BootStorage::RootPartitionDevice)["disk"]:""
	];
    }

    prep_boot_partition = "";
    // First take the partitions that Storage:: hinted us to take, then
    // consider the other prep_boot_partitions
    prep_boot_partitions = (list<string>)
      merge (
	     install_prep_boot_partitions,
	     prep_boot_partitions);
    // in the combined list, look for "usable" partitions:
    //	- if we require the boot partition to be on the same disk as
    //	  the root partition ("prep_same_disk_as_root"), select the
    //	  first prep partition from that disk
    //	- otherwise take the first prep partition in the list
    foreach (string partition, prep_boot_partitions, ``{
        if (prep_boot_partition == "") {
	    boolean usable = true;
	    if (prep_same_disk_as_root) {
	        map part_split = Storage::GetDiskPartition (partition);
		string part_disk = part_split["disk"]:"";
		if (! contains (root_disks, part_disk))
		    usable = false;
	    }
	    if (usable)
	        prep_boot_partition = partition;
	}
    });

    // For CHRP lilo can handle PReP partition on other disks now
    // If all above fails, take the first one then ...
    if ( (prep_boot_partition == "") && (getBoardType () == "chrp") ) {
      prep_boot_partition = prep_boot_partitions[0]:"";
    }

    y2milestone ("Selected PReP boot partition: %1", prep_boot_partition);
    BootCommon::activate = (prep_boot_partition != "");
    y2milestone ("Install bootloader: %1", BootCommon::activate);
}


/**
 * Initialize attributes of the board type
 */
void PRePInit () {
    y2milestone ("Initializing PReP attributes");
    prep_only_active = true;
    prep_only_iseries_vd = false;
    prep_only_primary = true;
    prep_same_disk_as_root = false;
    table_items = [ "__prep_location" ];
}


/**
 * Initialize attributes of the board type
 */
void CHRPInit () {
    y2milestone ("Initializing CHRP attributes");
    prep_only_active = true;
    prep_only_iseries_vd = false;
    prep_only_primary = true;
    // On CHRP, if there is no bootable partition on the disk containing
    // "/", there is CHRP-specific code in choosePrepBootPartition that
    // takes the first prep partition in the system.
    prep_same_disk_as_root = true;
    table_items = [ "__chrp_location", "__set_default_of" ];
}


/**
 * Helper function that executes a command with the shell, appending
 * stdout and stderr to a logfile. On error, it writes log entries to the
 * yast2 log.
 * @param command string command to execute
 * @param logfile string logfile for the commands output
 * @return boolean true on success
 */
boolean iSeriesExecute (string command, string logfile) ``{
    command = command + " >>" + logfile + " 2>&1";
    integer command_ret = (integer)SCR::Execute (.target.bash, command);
    if (command_ret != 0) {
        y2error ("Execution of command failed: %1, error code: %2", command, command_ret);
	string log = (string)SCR::Read (.target.string, logfile);
	y2error ("stderr and stdout of the command: %1", log);
	return false;
    }
    return true;
}


/**
 * Install the board-type-specific part of bootloader
 * @return boolean true on success
 */
global define boolean iSeriesWrite () {
    if (! BootCommon::activate)
        return true;

    // during installation (fresh or update), always install the ISERIES64
    // file into slot A as a "rescue system"
    if (Stage::initial ()) {
        string command = "";
	string my_log = "/var/log/YaST2/y2log_bootloader_iseries_slot_a";

	// bnc #409927 VUL-0: yast2: slideshow not checked cryptographically
	string src_filename = Pkg::SourceProvideDigestedFile(base_source, 1, "/ISERIES64", false);

	if (base_source == -1 || src_filename == nil) {
	    y2milestone ("Cannot write rescue kernel to slot A, base source not found");
	    return false;
	}

	string rescue_bootbinary = (string)SCR::Read (.target.tmpdir)
	    + "/rescue_bootbinary";
	string tg_rescue_bootbinary
	    = Installation::destdir + rescue_bootbinary;
	y2milestone ("Copying %1 to %2",
		     src_filename, tg_rescue_bootbinary);
	WFM::Execute (.local.bash, sformat ("/bin/cp %1 %2",
					    src_filename, tg_rescue_bootbinary));

	y2milestone("start writing rescue kernel to slot A ...");
	command = "time dd if=" + rescue_bootbinary
	  + " of=/proc/iSeries/mf/A/vmlinux bs=64k";
	if (! iSeriesExecute (command, my_log))
	    return false;

	if (! iSeriesExecute (
	    "dd if=/dev/zero of=/proc/iSeries/mf/A/cmdline bs=255 count=1",
	    my_log)
	    )
	  return false;

	// NOTE: on SLES10, the "root=" parameter is not handled by the
	// initrd in the ISERIES64 file. The initrd just boots up to a
	// shell.
	SCR::Execute (.target.bash,
		      "echo -en 'start_shell manual=1\\0' > /proc/iSeries/mf/A/cmdline");
	y2milestone("done writing rescue kernel to slot A.");
    }

    return true;
}


/**
 * Initialize attributes of the board type
 */
global define void iSeriesInit () {
    y2milestone ("Initializing iSeries attributes");
    prep_only_active = true;
    prep_only_iseries_vd = true;
    prep_only_primary = true;
    prep_same_disk_as_root = false;
    table_items = [ "__iseries_location" ];
}


// misc. functions

global define void initBoardType () {
    if (Arch::board_iseries ())
    {
	board_type = "iseries";
    }
    else if (Arch::board_prep ())
    {
	board_type = "prep";
    }
    else if (Arch::board_chrp ())
    {
	board_type = "chrp";
    }
    else if (Arch::board_mac_new ())
    {
        board_type="pmac";
    }
    else if (Arch::board_mac_old ())
    {
        board_type="pmac";
    }
    else
    {
	board_type = "unknown";
    }
    y2milestone ("setting board type to: %1", board_type);
}

global define string getBoardType () {
	if (board_type == nil)
	    initBoardType ();
	return board_type;
}

/**
  * Initialize the attribute of currently used board type
  */
 global define void currentBoardInit () {
     if (getBoardType () == "iseries")
     {
       iSeriesInit ();
     }
     else if (getBoardType () == "prep")
     {
       PRePInit ();
     }
     else if (getBoardType () == "chrp")
     {
       CHRPInit ();
     }
     // TODO other boards
 }


/**
 * Create section for bootable image
 * @param   title   string	the section name to create (untranslated)
 * @return	    map		describes the section
 */
global map<string,any> CreateImageSection (string title) {
    map<string,any> ret = BootCommon::CreateLinuxSection(title);
    //Do not use translated names, as we are happy if it work with kernel device
    ret["root"] = BootStorage::RootPartitionDevice;
    // bnc #217443
    ret["optional"] = "true";
    //do not use translated name FIXME this should be filtered out
    ret["name"] = title;
    return ret;
}

/**
  * Choose a boot partition on pmac
  * type == Apple_HFS|Apple_Bootstrap && size < 20 cyl
  * @return string device name of pmac boot partition
  */
string GoodPmacBootPartition() {
    y2milestone ("Detecting pmac boot partition");
    map<string,map> targetMap = (map<string,map>)Storage::GetTargetMap ();
    y2milestone ("TargetMap: %1", targetMap);

    list<string> boot_partitions = [];
    string selected_boot_partition = "";

    foreach (string dname, map ddata, targetMap, ``{
	list<map> partitions = ddata["partitions"]:[];
	y2milestone ("Partitions: %1", partitions);

	// does this device contain the root partition?
	boolean hasrootdev = ( find (map p, partitions, ``(
	    (! p["delete"]:false)
	    && p["device"]:"" == BootStorage::RootPartitionDevice
	    && !contains( [`lvm, `evms, `sw_raid], p["type"]:`primary )
	)) != nil );

	// find possible boot partitions
	partitions = filter (map p, partitions, ``(
	    (! p["delete"]:false)
	    && is (p["fsid"]:nil, integer)
	    // both partition types Apple_Bootstrap and Apple_HFS can be
	    // handled by PPC lilo; yast2-storage maps both to fsid 258
	    && (p["fsid"]:nil == 258)
	    && !contains( [`lvm, `evms, `sw_raid], p["type"]:`primary )
	));
	y2milestone ("Filtered existing partitions: %1", partitions);

	// bug #459860 - no boot partition found during fresh install
	// find the smallest partition
	integer max_size = 1000000000;
	integer iter = -1;
	integer min_position = -1;
	foreach (map p, partitions,
	{
		iter = iter +1;
		if (p["size_k"]:0 < max_size)
		{
			min_position = iter;
			max_size = p["size_k"]:0;
		}
	});

	// if any partition was found
	if (min_position > -1)
	{
		list <map> tmp_partitions = [];
		map partition = partitions[min_position]:$[];
		if (partition["size_k"]:0 < 160650)
			y2milestone("Partition smaller than 160650k: %1", partition);
		else
			y2warning("Partition is not smaller than 160650k: %1", partition);
		tmp_partitions = add(tmp_partitions, partition);
		partitions = tmp_partitions;
	}

	y2milestone ("Filtered existing partitions: %1", partitions);
	

	// found a boot partition on the same device as the root partition?
	if (hasrootdev && size (partitions) > 0
	    && selected_boot_partition == "")
	{
	    y2milestone ("Selected pmac boot partition %1 on device with root partition %2",
			 partitions[0, "device"]:"", BootStorage::RootPartitionDevice);
	    selected_boot_partition = partitions[0, "device"]:"";
	}

	// collect found boot partitions
	boot_partitions = (list<string>)merge (boot_partitions,
	    (list<string>)maplist (map p, partitions, ``(
		p["device"]:""
	    ))
	);
    });
    y2milestone ("Detected pmac boot partitions: %1", boot_partitions);
    pmac_boot_partitions = boot_partitions;
    if (selected_boot_partition == "")
    {
        selected_boot_partition = boot_partitions[0]:"";
    }
    y2milestone ("Selected pmac boot partition: %1", selected_boot_partition);
    return selected_boot_partition;
}

/**
  * Propose the location of the root device on disk and the boot device (if
  * any), according to the subarchitecture.
  * Results are stored in global variables.
  *
  */
global void LocationProposal () {
    BootCommon::DetectDisks ();
    // del_parts is used by FixSections() in lilolike.ycp (imported by BootCommon.ycp)
    BootCommon::del_parts = BootStorage::getPartitionList (`deleted, "ppc");

    if (BootCommon::DisksChanged ()) {
        y2milestone ("Reconfiguring locations");
	BootCommon::DetectDisks ();
    }

    if (updatePrepBootPartitions () || prep_boot_partition == "")
    {
	// TODO warning to user
	choosePrepBootPartition ();
    }

    switch (getBoardType()) {
    case ("chrp"):
	BootStorage::BootPartitionDevice = prep_boot_partition;
        break;
    case ("prep"): 
	BootStorage::BootPartitionDevice = prep_boot_partition;
        break;
    case ("iseries"):
	BootStorage::BootPartitionDevice = prep_boot_partition;
        break;
    case ("pmac") :
	BootStorage::BootPartitionDevice = GoodPmacBootPartition();
        break;
    default:
        y2error("Unknown ppc architecture");
    }

    // These need to be set, for POWERLILO probably only to interface with
    // autoyast, others base subsequent decisions on this.
    // See ConfigureLocation() in lilolike.ycp.
    //
    // Mini-discussion: If autoyast is mainly used to clone configs, the
    // loader_device and repl_mbr interface is enough, because loader_device
    // simply contains the name of the device (partition, disk MBR, RAID
    // device) to use for the bootloader.
    // But if autoyast some day is used to transport configurations to less
    // similar machines and setups, or to specify some sort of generic setup
    // with special settings that will work on most machines, it may (or may
    // not) be helpful to be able to specify boot_* variables in the autoyast
    // file. This may apply better to the boot_* variables in BootGRUB.ycp
    // though.
    // FIXME: what about loader_location (aka selected_location internally)?
    BootCommon::loader_device = BootStorage::BootPartitionDevice;
    BootCommon::activate = true;
    y2milestone("Boot partition is %1", BootCommon::loader_device);
}

/**
 * Propose sections to bootloader menu
 * modifies internal sreuctures
 */
global void CreateSections () {
    map<string,any> linux = CreateImageSection ("linux");

    // FIXME: create an 'other' section for MACs to boot MacOS

    BootCommon::sections = [ linux, ];
}

/**
 * Propose global options of bootloader
 * modifies internal structures
 */
global void CreateGlobals () {

    BootCommon::globals = $[
	"activate": "true",
	"default" : BootCommon::sections[0, "name"]:"",
	"timeout" : "8",
    ];

    map<string, string> boot_map = $[];

    y2milestone("RootPartDevice is %1",BootStorage::RootPartitionDevice);
    switch(getBoardType()){
    case ("chrp") :
        boot_map = $[
	    "boot_chrp_custom" : BootStorage::BootPartitionDevice,
        ];
        break;
    case ("prep") :
        boot_map = $[
	    "boot_prep_custom" : BootStorage::BootPartitionDevice,
	];
        break;
    case ("pmac") :
        boot_map = $[
	    "boot_pmac_custom" : BootStorage::BootPartitionDevice,
        ];
        break;
    case ("iseries") :
        boot_map = $[
	    "boot_slot"		 : "B",
	    // FIXME: what file should be used here?
            "boot_file"		 : "/tmp/suse_linux_image",
	 ];

	// If we have an empty BootPartitionDevice on iseries, this means:
	// do not boot from BootPartitionDevice but from some other place.
	// Do not pass down to perl-Bootloader, lilo fails on an empty "boot =" line.
	if (BootStorage::BootPartitionDevice != nil &&
	    BootStorage::BootPartitionDevice != "") {
	    boot_map["boot_iseries_custom"] = BootStorage::BootPartitionDevice;
	}
        break;
    default:
        y2error("Unknown ppc architecture");
    }

    // Finally merge results into "globals": new values replace old ones
    BootCommon::globals = (map<string, string>) union(BootCommon::globals, boot_map);

}

/**
 * Save the ID of the base installation source
 * modifies internal variable
 */
global void SaveInstSourceId () {

    base_source = -1;

    // Find the source ID of the base product:
    // list all products
    list<map<string,any> > products =
	Pkg::ResolvableProperties ("", `product, "");
    y2internal ("products: %1", products);
    // filter products to be installed
    products = filter (map<string,any> p, products, {
	return p["source"]:-1 != -1;
    });
    // get base products
    list<map<string,any> > base_products =
	filter (map<string,any> p, products, {
	    return p["category"]:"" == "base";
	});
    if (size (base_products) == 0)
	base_products = products; // just to be safe in case of a bug...
    list<integer> sources = maplist (map<string,any> p, base_products, {
	return p["source"]:-1;
    });
    y2internal ("remaining products: %1, sources: %2",
	products, sources);
    sources = sort (sources);
    base_source = sources[0]:-1;

    y2milestone ("Base source: %1", base_source);
}

/** bnc #439674 Autoyast install of Cell blades fails to install bootloader
 * The function update global settings for bootloader
 * if there is used autoyast The basic problem is that there is missing
 * boot_* , timeout etc.
 * If there missing necessary information they will be added
 *
 */
void UpdateGlobalsInAutoInst()
{
	if (!haskey(BootCommon::globals, "timeout"))
		BootCommon::globals["timeout"] = "8";

	if (!haskey(BootCommon::globals, "activate"))
		BootCommon::globals["activate"] = "true";

	// if there missing boot_* -> then propose it
	if ((!haskey(BootCommon::globals, "boot_chrp_custom")) &&
	    (!haskey(BootCommon::globals, "boot_prep_custom")) &&
	    (!haskey(BootCommon::globals, "boot_pmac_custom")) &&
	    (!haskey(BootCommon::globals, "boot_iseries_custom")))
	{
		string arch = getBoardType ();
		switch(arch) {
			case ("prep"):
				BootCommon::globals["boot_prep_custom"] = BootStorage::BootPartitionDevice;
			break;

			case ("pmac"):
				BootCommon::globals["boot_pmac_custom"] = BootStorage::BootPartitionDevice;
			break;

			case ("iseries"):
				BootCommon::globals["boot_slot"] = "B";
				BootCommon::globals["boot_file"] = "/tmp/suse_linux_image";

				if (BootStorage::BootPartitionDevice != nil &&
				   BootStorage::BootPartitionDevice != "") 
				{
					BootCommon::globals["boot_iseries_custom"] = BootStorage::BootPartitionDevice;
				}
			break;
			default:
				BootCommon::globals["boot_chrp_custom"] = BootStorage::BootPartitionDevice;
			break;
		}

	}

}

// general functions

/**
  * Propose bootloader settings
  */
global define void Propose () {
    y2debug ("Started propose: Glob: %1, Sec: %2",
	BootCommon::globals, BootCommon::sections);

    // Need to remember inst source ID now to get the ISERIES64 file from the
    // inst source later on (see Bug #165497, Comment #16). This won't work
    // later during inst_finish, so we need to do it earlier -- only the
    // proposal is a possible place.
    SaveInstSourceId();

    // FIXME: make modern code out of these conditionals
    //        - comments
    //        - simplify
    //        - check validity
    boolean initial_propose = true;
    if (BootCommon::was_proposed)
    {
	// FIXME: autoyast settings are simply Import()ed and was_proposed is
	// set to true. The settings for the current board still need to be
	// initialized though. We do this every time the bootloader proposal is
	// called, because it also does not harm (results for the board
	// detection are cached both in Arch.ycp and in our variable
	// board_type.) To fix: make the "where does the information come
	// from", when, more clear and obvious (in the code and/or in docs).
	if (Mode::autoinst ())
	{
	    currentBoardInit ();
	}
	initial_propose = false;
    }
    else
    {
	currentBoardInit ();
    }
    y2milestone ("board type is: %1", board_type);

    // Get root and boot partition (if any)
    LocationProposal();

    if (BootCommon::sections == nil || size (BootCommon::sections) == 0)
    {
        CreateSections ();  // make an initial proposal for at least one section
        BootCommon::kernelCmdLine = Kernel::GetCmdLine ();
    }
    else
    {
	if (Mode::autoinst ())
	{
	    y2debug ("Nothing to do in AI mode if sections exist");
	    // bnc #439674 Autoyast install of Cell blades fails to install bootloader
	    UpdateGlobalsInAutoInst();
	}
	else
	    BootCommon::FixSections (BootPOWERLILO::CreateSections);
    }

    if (BootCommon::globals == nil ||
	// consider globals empty even if lines_cache_id is present
	size (filter(string key, any v, BootCommon::globals, {
		return key != "lines_cache_id";
	    })) == 0)
    {
        CreateGlobals ();
    }
    else
    {
	if (Mode::autoinst ())
	{
	    y2debug ("Nothing to do in AI mode if globals are defined");
	}
	else
	    BootCommon::FixGlobals ();
    }

    y2milestone ("Proposed sections: %1", BootCommon::sections);
    y2milestone ("Proposed globals: %1", BootCommon::globals);
}



/**
  * Export bootloader settings to a map
  * @return bootloader settings
  */
global define map Export () {
    map exp = $[
		"global": BootCommon::remapGlobals(BootCommon::globals),
		"sections" : BootCommon::remapSections(BootCommon::sections),
		"activate" : BootCommon::activate,
	    ];
    return exp;
}


/**
  * Import settings from a map
  * @param settings map of bootloader settings
  * @return boolean true on success
  */
global define boolean Import (map settings) {
    BootCommon::globals = settings["global"]:$[];
    BootCommon::sections = settings["sections"]:[];
    BootCommon::activate = settings["activate"]:false;
    return true;
}


/**
 * Read settings from disk
 * @param reread boolean true to force reread settings from system
 * @param avoid_reading_device_map do not read new device map from file, use
 * internal data
 * @return boolean true on success
 */
global boolean Read (boolean reread, boolean avoid_reading_device_map) {
    BootCommon::InitializeLibrary (reread, "ppc");
    if (reread) {
	BootCommon::ReadFiles (avoid_reading_device_map);
    }

    boolean ret = BootCommon::Read (false, avoid_reading_device_map);
    y2milestone (":: Read globals: %1", BootCommon::globals);

    //importMetaData();

    return ret;
}


/**
 * Reset bootloader settings
 */
global define void Reset (boolean init) {
    // Reset global variables to default values
    prep_boot_partition = "";
    prep_boot_partitions = [];
    install_prep_boot_partitions = [];
    BootCommon::Reset (init);
}


/**
 * Save all bootloader configuration files to the cache of the PlugLib
 * PlugLib must be initialized properly !!!
 * @param clean boolean true if settings should be cleaned up (checking their
 *  correctness, supposing all files are on the disk
 * @param init boolean true to init the library
 * @param flush boolean true to flush settings to the disk
 * @return boolean true if success
 */
global boolean Save (boolean clean, boolean init, boolean flush) {
    boolean ret = true;

    // FIXME: this is currently a copy from BootCommon::Save
    if (clean)
    {
	BootCommon::RemoveUnexistentSections ("", "");
	BootCommon::UpdateAppend ();
    }

    // check if there is selected "none" bootloader
    string bl = BootCommon::getLoaderType (false);

    if (bl == "none")
    {
	BootCommon::InitializeLibrary (init, bl);
	return true;
    }

    if (! BootCommon::InitializeLibrary (init, "ppc"))
        // send current disk/partition information to perl-Bootloader
	BootCommon::SetDiskInfo ();

    // convert
    map<string,string> my_globals = mapmap (string k , string v, BootCommon::globals, {
	if ((k == "stage1_dev") || (regexpmatch(k, "^boot_.*custom$" )))
	    return $[k : BootStorage::Dev2MountByDev(v)];
	else
	    return $[k : v];
    });

    // FIXME: remove all mountpoints of type 'boot/boot' through some Storage::<func>

    // FIXME: set one mountpoint 'boot/boot' for every boot target means all
    // partitions in 'boot_<arch>_custom' and 'clone' (chrp)

    // ret = ret && BootCommon::SetDeviceMap (device_mapping);

    // bnc #450506 root=kernelname in lilo.conf after upgrade
    BootCommon::sections = BootCommon::remapSections(BootCommon::sections);

    ret = ret && BootCommon::SetSections (BootCommon::sections);
    ret = ret && BootCommon::SetGlobal (my_globals);
    if (flush)
	ret = ret && BootCommon::CommitSettings ();

    //importMetaData();

    BootCommon::WriteToSysconf(false);
    return ret;
}


/**
  * Display bootloader summary
  * @return a list of summary lines
  */
global define list<string> Summary () {
    list<string> result = [];

    // FIXME:
    //	- evaluate and use the text from iSeriesSummary(), PRePSummary() and
    //	  CHRPSummary()
    //  - add the cases for mac_old and mac_new (see BootPPC::Summary())

    // summary text, %1 is bootloader name
    result = add(
	result,
	sformat(
	    _("Boot loader type: %1"),
	    BootCommon::getLoaderName (BootCommon::getLoaderType (false), `summary)
	)
    );

    // summary text for boot loader locations, sum up all locations to one string
    string boot_loader_locations =
        mergestring(
	    filter( string bll,
		maplist( string key, any value, BootCommon::global_options, {
		    return (substring(key,0,5) == "boot_")
		            ? BootCommon::globals[key]:"" : "";
		}),
	        { return bll != ""; }
	    ),
	    ", "
	);
    result = add (result, sformat (_("Location: %1"), boot_loader_locations));

    list<string> sects = [];
    foreach (map<string,any> s, BootCommon::sections, {
        string title = s["name"]:"";
	// section name "suffix" for default section
        string def = (title == BootCommon::globals["default"]:"")
	  ? _(" (default)")
	  : "";
        sects = add (sects, String::EscapeTags (sformat ("+ %1%2", title, def)));
    });
    // summary text. %1 is list of bootloader sections
    result = add (result, sformat (_("Sections:<br>%1"),
        mergestring (sects, "<br>")));

    // FIXME: does the following code make any sense for ppc? (see also #163387)
    // It seems not. (We do not do this, cf. jplack.) Keeping the code cadaver
    // around until finally ready for removal.
    // if (BootCommon::loader_device == "/dev/null")
    //    // summary text
    //    result = add (result,
//		_("Do not install boot loader; just create configuration files"));
    return result;
}


/**
 * Update read settings to new version of configuration files
 */
global void Update () {

   /**
    * Firstly update sections of bootloader configuration and modify internal
    * structures as needed. This means right now:
    *
    * - no change of "resume=" parameter in append entry, not used on ppc yet
    * - delete console= parameters as console autodetection now works
    */

    // This function has been copied from lilolike.ycp::UpdateSections and
    // adapted to conform with the image parameter
    // BootPOWERLILO.ycp/perl-Bootloader uses. Some unneeded code has been
    // removed.
    // FIXME: SLES9 -> SLES10 update: check loader_type = lilo in
    // /etc/sysconfig/bootloader

    // take current sections as starting point
    list<map<string,any> > updated_sections = BootCommon::sections;
    boolean linux_resume_added = false;

    map<string,any> default_sect = CreateImageSection("linux");
    string default_name = default_sect["name"]:"";

    // assumption is that all of the following section names ar "good" names
    // meaning that we will return a valid section description from
    // CreateImageSection for them.
    list<string> sections_to_recreate = ["linux"];

    updated_sections = maplist (map<string,any> s, updated_sections, {
	string name = s["name"]:"";
	string oname = s["original_name"]:name;

	// if we find a section that looks like it has been initially proposed
	// from the installer, replace with the actual "good" proposal
	if (contains(sections_to_recreate, oname)) {
	    sections_to_recreate = filter (string this_name,
		sections_to_recreate, ``(this_name != oname)
	    );
	    // check for a new global default if oname != name
	    if ( name == BootCommon::globals["default"]:"" ) {
	        // we assume that the new name produced by CreateImageSection
	        // will be oname
		BootCommon::globals["default"] = oname;
	    }
	    return CreateImageSection(oname);
	}

	// else adjust the entries of the found section according to some
	// fancy rules
	foreach (string key, ["image", "initrd"], {
	    string value = s[key]:"";
	    // FIXME: check whether this is code for update from SLES8?
	    //        then we would delete it.
	    if (regexpmatch (value, "^.*\.shipped.*$"))
	    {
		value = regexpsub (value,
		    "^(.*)\.shipped(.*)$", "\\1\\2");
	    }
	    else if (regexpmatch (value, "^.*\.suse.*$"))
	    {
		value = regexpsub (value,
		    "^(.*)\.suse(.*)$", "\\1\\2");
	    }
	    s[key] = value;
	});


	// update root= entry in selected sections as the device naming
	// changes in the linux kernel from time to time ...
	if (contains (BootCommon::update_section_types, oname) && haskey(s, "root"))
	{
	    y2milestone ("Updating root device of section %1", name);
	    s["root"] = BootCommon::UpdateDevice (s["root"]:"");
	}

	// handle the append line
	string append = s["append"]:"";

	// FIXME: how should we handle root= entries in append= lines?

	// add additional kernel parameters to the end of the append entry
	// of special image section 'linux'
	//
	if (oname == "linux") {
	    foreach (string o, BootCommon::ListAdditionalKernelParams (), {
	        append = BootCommon::setKernelParamToLine (append, o, "false");
	    });
	    append = append + " " + BootCommon::GetAdditionalKernelParams ();

	    if (BootCommon::getKernelParamFromLine (append, "splash") == "false")
	        append = BootCommon::setKernelParamToLine (append, "splash", "silent");
	}

	// remove console= entries from kernel parameters, console auto
	// detection now works. For special sections take what's given on boot
	// command line
	string console = "false"; // false means delete to 'setKernelParamToLine'
	if (contains (BootCommon::update_section_types, oname))
	{
	    console = BootCommon::getKernelParamFromLine (Kernel::GetCmdLine(), "console") ;
	}
	append = BootCommon::setKernelParamToLine (append, "console", console);

	// finally append entry is written back
	if (append != "")
	    s["append"] = append;
	else
	    s = remove(s, "append");

	return s;
    });

    // if there was no original section matching the sections we want to
    // recreate, so do prepend or append newly created sections to the list of
    // updated sections
    foreach (string section_name, sections_to_recreate, {
        map<string,any> new_section =  CreateImageSection(section_name);
	if (section_name == "linux")
	    updated_sections = prepend (updated_sections, new_section);
	else
	    updated_sections = add (updated_sections, new_section);
    });

    BootCommon::sections = updated_sections;
    y2milestone("finished updating sections: %1", updated_sections);
    // End of UpdateSections ();

   /**
    * Secondly update global settings of bootloader configuration:
    *
    * - no change of 'activate'
    * - no change of 'timeout'
    * - no change of default section
    * - no change of default initrd
    * - update device names that might have changed in as needed
    * - delete console= parameters as console autodetection now works
    */
    BootCommon::loader_device
	= BootCommon::UpdateDevice (BootCommon::loader_device);

    // update device naming of default root and boot_* entries
    foreach (string key, ["root", "boot_prep_custom", "boot_chrp_custom",
			  "boot_iseries_custom","boot_pmac_custom", "clone"], {
	if (haskey(BootCommon::globals, key)) {
	    y2milestone ("Updating global %1= setting, currently %2",
			 key, BootCommon::globals[key]:"");
	    BootCommon::globals[key] =
	      BootCommon::UpdateDevice (BootCommon::globals[key]:"");
	}
    });

    // remove console= entries from globals, console auto detection now works
    if (haskey (BootCommon::globals, "append")) {
        string append = BootCommon::globals["append"]:"";
	append = BootCommon::setKernelParamToLine (append, "console", "false");
	if (append != "")
	    BootCommon::globals["append"] = append;
	else
	    BootCommon::globals = remove(BootCommon::globals, "append");
    }
}

/**
  * Write bootloader settings to disk
  * @return boolean true on success
  */
global define boolean Write () {

    if (getBoardType () == "iseries")
    {
	iSeriesWrite ();
    }

    boolean ret = BootCommon::UpdateBootloader ();

    ret = ret && BootCommon::InitializeBootloader ();
    if (ret == nil)
	ret = false;
    return ret;
}


global map<string,symbol()> Dialogs () {
    // PPC definitly needs other text modules
    return $[
	"loader"	: PPCDetailsDialog,
    ];
}

/**
  * Set section to boot on next reboot
  * @param section string section to boot
  * @return boolean true on success
  */
global define boolean FlagOnetimeBoot (string section)
{
    map result = (map)SCR::Execute (.target.bash_output,
				    sformat ("/sbin/lilo -R \"%1\"", section));
    y2milestone ("lilo returned %1", result);
    return (result["exit"]:-1 == 0);
}

list<string> ppc_section_types(){
  if (Arch::board_iseries()){
    return ["image","other"];
  }

  return ["image"];
}

/**
 * Return map of provided functions
 * @return map map of functions (eg. $["write":BootPOWERLILO::Write])
 */
global map<string, any> GetFunctions () {
    return $[
	"export"		: Export,
	"import"		: Import,
	"read"			: Read,
	"reset"			: Reset,
	"propose"		: Propose,
	"save"			: Save,
	"summary"		: Summary,
	"update"		: Update,
	"write"			: Write,
	"widgets"		: ppcWidgets,
	"dialogs"		: Dialogs,
	"section_types"		: ppc_section_types,
	"flagonetimeboot"	: FlagOnetimeBoot,
    ];
}

/**
  * Initializer of PowerLILO bootloader
  */
global void Initializer () {
    y2milestone ("Called PowerLILO initializer");
    BootCommon::current_bootloader_attribs = $[
	"propose" : true,
	"read" : true,
	"scratch" : true,
	"bootloader_on_disk" : true,
    ];

    BootCommon::InitializeLibrary (false, "ppc");
}

/**
  * Constructor
  */
global void BootPOWERLILO () {
    BootCommon::bootloader_attribs["ppc"] = $[
	"required_packages"	: ["lilo"],
	"loader_name"		: "ppc",
	"initializer"		: BootPOWERLILO::Initializer,
    ];
}

}

/*
 * Local variables:
 *     mode: ycp
 *     mode: font-lock
 *     mode: auto-fill
 *     indent-level: 4
 *     fill-column: 78
 * End:
 */


ACC SHELL 2018