/**
 * File:
 *      modules/BootPPC.ycp
 *
 * Module:
 *      Bootloader installation and configuration
 *
 * Summary:
 *      Module containing specific functions for PPC configuration
 *      and installation
 *
 * Authors:
 *      Jiri Srain <jsrain@suse.cz>
 *      Klaus Kaempf <kkaempf@suse.de>
 *      Anas Nashif <nashif@suse.de>
 *
 * $Id: BootPPC.ycp 21211 2005-02-04 10:08:05Z jsrain $
 *
 */

{
    module "BootPPC";

    textdomain "bootloader";

    import "Arch";
    import "BootCommon";
    import "Installation";
    import "Kernel";
    import "Mode";
    import "Stage";
    import "Storage";

    // ppc specific global settings

    // change the OpenFirmware variable 'boot-device' on CHRP or new PowerMacs
    global boolean  of_defaultdevice = true;

    // 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 = "/dev/null";

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

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

    // iSeries specific global settings

    // filename for the iSeries kernel, for streamfile *STMF booting
    global string   iseries_streamfile  = "/boot/suse_linux_bootfile";

    // write the install kernel to slot A, for rescue purpose
    global boolean  iseries_write_slot_a = false;

    // write the kernel to slot B
    global boolean  iseries_write_slot_b = false;

    // write the kernel to an 41 PReP boot partition
    global boolean  iseries_write_prepboot = false;

    // write the kernel to file, this must be transfered to OS/400
    global boolean  iseries_write_streamfile = false;

    // 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 = "";
    global string root_device = "";

    string board_type = nil;

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

    include "bootloader/routines/popups.ycp";
    include "bootloader/routines/routines.ycp";
    include "bootloader/ppc/misc.ycp";
    include "bootloader/ppc/prep.ycp";
    include "bootloader/ppc/chrp.ycp";
    include "bootloader/ppc/iseries.ycp";
    include "bootloader/ppc/widgets.ycp";


// 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 = "mac_new";
	}
        else if (Arch::board_mac_old ())
        {
	    board_type = "mac_old";
	}
        else if (Arch::board_mac_nubus ())
        {
	    board_type = "mac_nubus";
	}
	else
	{
	    board_type = "unknown";
	}
    }

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

    global define void setBoardType (string board) ``{
	board_type = board;
	currentBoardInit ();
    }

    /**
      * 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
    }

    /**
      * Write the settings specific to current board
      * Cannot use SCR!!!
      * @return boolean true on success
      */
    global define boolean currentBoardRead () ``{
	if ( getBoardType () == "iseries" )
	{
	    return iSeriesRead ();
	}
	else if ( getBoardType () == "prep" )
	{
	    return PRePRead ();
	}
	else if ( getBoardType () == "chrp" )
	{
	    return CHRPRead ();
	}
	// TODO other boards

	return true;
    }

/**
 * Update the settings - process board-specific operations
 * @return boolean true on success
 */
global boolean currentBoardUpdate () {
	if ( getBoardType () == "iseries" )
	{
	    return iSeriesUpdate ();
	}
	else if ( getBoardType () == "prep" )
	{
	    return PRePUpdate ();
	}
	else if ( getBoardType () == "chrp" )
	{
	    return CHRPUpdate ();
	}
	// TODO other boards

	return true;
}

    /**
      * Read the settings specific to current board
      * Cannot use SCR!!!
      * @return boolean true on success
      */
    global define boolean currentBoardSave () ``{
	if ( getBoardType () == "iseries" )
	{
	    return iSeriesSave ();
	}
	else if ( getBoardType () == "prep" )
	{
	    return PRePSave ();
	}
	else if ( getBoardType () == "chrp" )
	{
	    return CHRPSave ();
	}
	// TODO other boards

	return true;
    }

    /**
      * Create linux section
      * @param title strign section title (linux, failsafe, memtest)
      * @return section description structure
      */
    global define list<map> createLinuxSection (string title) ``{
	if (title != "linux")
	{
	    return [];
	}
	string append = sformat ("%1 %2 %3",
	    Kernel::GetCmdLine (),
	    BootCommon::GetAdditionalKernelParams (),
	    "selinux=0");
	// always server product, FIXME when it changes
	append = BootCommon::setKernelParamToLine (append, "desktop", "false");
	list<map> section = [
	    $[ "key" : "image", "value" : "/boot/" + Kernel::GetBinary ()],
	    $[ "key" : "label", "value" : "linux" ],
	    $[ "key" : "root", "value" : BootCommon::RootPartitionDevice ],
	    $[ "key" : "initrd", "value" : "/boot/initrd" ],
	    $[ "key" : "append", "value" : append ],
	    $[ "key" : "__intern__", "value" : $[
		"auto" : true,
		"changed" : false,
		"devs" : [BootCommon::RootPartitionDevice],
		"type" : title,
	    ]],
        ];
	return section;
    }

    /**
      * Propose sections to bootloader menu
      * modifies internal sreuctures
      * FIXME very similar to the one in BootLILO
      */
    global define void createSections () ``{
	list<list<map> > out = [
	    createLinuxSection ("linux")
	];
	list<string> others = (list<string>)Storage::GetForeignPrimary();
	if (others != nil)
	{
	    if (size (others) > 0)
	    {
		integer index = 0;
		foreach (string o, others, ``{
		    index = index + 1;
		    list<string> parts = splitstring (o, " ");
		    while (parts[0]:" " == "")
			parts = remove (parts, 0);
		    string dev = parts[0]:"";
		    parts = remove (parts, 0);
		    string label = mergestring (parts, " ");
		    if (label == "")
		    {
			label = "macos";
			if (index > 1)
			{
			    label = sformat ("macos %1", index);
			}
		    }

		    // don't add rewritten location (#19990)
		    // FIXME may not be sufficient for Macs
		    if (dev != "" && dev != prep_boot_partition)
		    {
			list<map> m = [
			    $["key" : "label",
				"value" : BootCommon::removeBlanks (label)],
			    $[ "key" : "other", "value" : dev],
			    $[ "key" : "__intern__", "value" : $[
				"auto" : true,
				"changed" : false,
				"devs" : [dev],
				"type" : label,
			     ]],
			];
			out = (list<list<map> >)add (out, m);
		    }
		});
	    }
	}
	out = (list<list<map> >)filter (list<map> s, out,
	    ``(s != [] && s != nil));
	BootCommon::sections = out;
    }

    /**
      * Propose global options of bootloader
      * modifies internal structures
      */
    global define void createGlobals () ``{
	BootCommon::globals = [
	    $[ "key" : "default", "value" : "linux"],
	    $[ "key" : "timeout", "value" : 100],
	];
    }

    /**
      * Update sections of bootloader menu
      * modifies internal sreuctures
      * @param replace boolean true if old sectinos shall be replaced
      */
    global define void updateSections (boolean replace) ``{
	BootCommon::liloUpdateSections (replace, BootPPC::createLinuxSection);
	return;
    }

    /**
      * Update global options of bootloader
      * modifies internal sreuctures
      */
    global define void updateGlobals () ``{
	BootCommon::globals = maplist (map e, BootCommon::globals, ``{
	    if (e["key"]:"" == "boot")
		e["value"] = BootCommon::UpdateDevice (e["value"]:"");
	    return e;
	});
    }

    /**
      * Fix sections of bootloader menu during proposal regeneration
      * modifies internal sreuctures
      * @param disks_changed boolean should be true if bootloader location
      *   relevant disk locations have changed
      */
    global define void fixSections (boolean disks_changed) ``{
	BootCommon::liloFixSections (BootPPC::createSections);
	return;
    }

    /**
      * Fix global options of bootloader during proposal regeneration
      * modifies internal sreuctures
      * @param disks_changed boolean should be true if bootloader location
      *   relevant disk locations have changed
      */
    global define void fixGlobals (boolean disks_changed) ``{
	BootCommon::liloFixGlobals ();
    }

    /**
      * Create strings representing cfg. files for by-hand editation
      * @return map filename -> contents
      */
    global define map<string,string> GetFiles() ``{
	return $[]; // FIXME remove
	currentBoardSave ();
// TODO rewrite using the bootloader library
//        BootCommon::saveCfgFileToAgent ();
        return $[
            "/etc/lilo.conf" : (string)SCR::Read (.lilo.tostring),
        ];
    }

    /**
      * Store strings representing cfg. files to internal structures
      * @param files map filename -> contents
      */
    global define void SetFiles (map<string,string> files) ``{
	return; // FIXME remove
	if (SCR::Read (.lilo.tostring) != files["/etc/lilo.conf"]:"")
	    BootCommon::files_edited = true;
        SCR::Write (.lilo.fromstring, files["/etc/lilo.conf"]:"");
// TODO rewrite using the bootloader library
//        BootCommon::loadCfgFileFromAgent ();
	currentBoardRead ();
    }

    /**
      * Get map of bootloader options
      * FIXME this was just copied from LILO, changes are expected
      * @return map of options
      */
    global define map GetOptions () ``{
	map opts = $[
	    `global: [
	        "append",
	        "backup",
	        "boot",
	        "change-rules",
	        "compact",
	        "default",
	        "delay",
	        "disk",
	        "disktab",
	        "fix-table",
		"force-backup",
	        "ignore-table",
	        "install",
	        "lba32",
	        "linear",
	        "lock",
	        "map",
	        "menu-title",
	        "menu-scheme",
	        "message",
	        "nowarn",
	        "optional",
	        "password",
	        "prompt",
	        "restricted",
	        "serial",
	        "timeout",
	        "verbose",
	        "vga",
	    ],
	    "image" : [
	        "append",
	        "initrd",
	        "literal",
	        "ramdisk",
	        "read-only",
	        "read-write",
	        "root",
	        "vga",
	        "label",
	        "alias",
	        "lock",
	        "optional",
	        "password",
	        "restricted",
	    ],
	    "other" : [
	        "loader",
	        "table",
	        "change",
	        "map-drive",
	        "unsafe",
	        "label",
	        "alias",
	        "lock",
	        "optional",
	        "password",
	        "restricted",
	    ]
	];
	return opts;
    }


// general functions

    /**
      * Export bootloader settings to a map
      * @return bootloader settings
      */
    global define map Export () ``{
	map ret = BootCommon::Export ();
	ret["board_type"] = getBoardType ();
	if (getBoardType () == "iseries")
	{
	    ret["iseries_streamfile"] = iseries_streamfile;
	    ret["iseries_write_slot_a"] = iseries_write_slot_a;
	    ret["iseries_write_slot_b"] = iseries_write_slot_b;
	    ret["iseries_write_streamfile"] = iseries_write_streamfile;
	    ret["iseries_write_prepboot"] = iseries_write_prepboot;
	    ret["prep_boot_partition"] = prep_boot_partition;
	}
	else if (getBoardType () == "prep")
	{
	    ret["prep_boot_partition"] = prep_boot_partition;
	}
	else if (getBoardType () == "chrp")
	{
	    ret["prep_boot_partition"] = prep_boot_partition;
	    ret["of_defaultdevice"] = of_defaultdevice;
	}
	return ret;
    }
    /**
      * Import settings from a map
      * @param settings map of bootloader settings
      */
    global define boolean Import (map settings) ``{
	BootCommon::Import (settings);
	iseries_streamfile
	    = settings["iseries_streamfile"]:"/boot/suse_linux_bootfile";
	iseries_write_streamfile = settings["iseries_write_streamfile"]:false;
	iseries_write_prepboot = settings["iseries_write_prepboot"]:false;
	iseries_write_slot_a = settings["iseries_write_slot_a"]:false;
	iseries_write_slot_b = settings["iseries_write_slot_b"]:false;
	prep_boot_partition = settings["prep_boot_partition"]:"/dev/null";
	of_defaultdevice = settings["of_defaultdevice"]:true;
	if (! Mode::config ())
	{
	    board_type = nil;
	    string bt = getBoardType ();
	    if (bt != (string)(settings["board_type"]:nil))
	    {
		y2error ("Importing data for board %1, current is %2",
		    (string)(settings["board_type"]:nil),
		    bt);
	    }
	}
	setBoardType (settings["board_type"]:"");
	currentBoardInit ();
	return true;
    }
    /**
      * Propose bootloader settings
      */
    global define void Propose () ``{
	boolean initial_propose = true;
	if (BootCommon::was_proposed)
	{
	    initial_propose = false;
	}
	else
	{
	    currentBoardInit ();
	}
        BootCommon::DetectDisks ();
	BootCommon::del_parts = BootCommon::getPartitionList (`deleted);
	boolean disks_changed = BootCommon::RefreshDisks();

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

        if ( getBoardType () == "iseries" )
        {
	    iSeriesPropose (initial_propose);
        }
        else if ( getBoardType () == "prep" )
        {
	    PRePPropose (initial_propose);
        }
        else if ( getBoardType () == "chrp" )
        {
	    CHRPPropose (initial_propose);
	}

	if (BootCommon::sections == nil || size (BootCommon::sections) == 0)
	{
	    createSections ();
	    BootCommon::kernelCmdLine = Kernel::GetCmdLine ();
	}
	else
	{
            if (Mode::autoinst ())
            {
		y2debug ("nothing to to in AI mode if sections exist");
                // TODO whatever will be needed
            }
            else
		fixSections (disks_changed);
	}
	if (BootCommon::globals == nil || size (BootCommon::globals) == 0)
	{
	    createGlobals ();
	}
	else
	{
            if (Mode::autoinst ())
            {
		y2debug ("nothing to to in AI mode if globals are defined");
                // TODO whatever will be needed
            }
            else
		fixGlobals (disks_changed);
	}
    }

    /**
      * Read settings from disk
      * @return boolean true on success
      */
    global define boolean Read () ``{
	BootCommon::DetectDisks ();
	currentBoardInit ();
	updatePrepBootPartitions ();
        choosePrepBootPartition ();

	boolean ret = BootCommon::Read ();
	ret = currentBoardRead () && ret;
	BootCommon::finishReading ();
	return ret;
    }

    /**
      * Reset bootloader settings
      */
    global define void Reset (boolean init) ``{
	BootCommon::Reset (init);
	of_defaultdevice = true;
	iseries_streamfile = "/boot/suse_linux_bootfile";
	iseries_write_slot_a = false;
	iseries_write_slot_b = false;
	iseries_write_prepboot = false;
	iseries_write_streamfile = false;
	prep_boot_partitions = [];
	prep_boot_partition = "/dev/null";
	BootCommon::activate = true;
	currentBoardInit ();
    }

    /**
      * Save all bootloader configuration files
      * @return boolean true if success
      */
    global define boolean Save () ``{
        if (!( getBoardType () == "prep" || getBoardType () == "chrp"
	    || getBoardType () == "mac_new" || getBoardType () == "iseries" ))
	{
            return true;
	}
	boolean ret = currentBoardSave ();

        SCR::Execute (.target.bash, "test -f /etc/lilo.conf && /bin/cp /etc/lilo.conf /etc/lilo.conf.old");
        SCR::Execute (.target.bash, "/usr/bin/touch /etc/lilo.conf");

//	BootCommon::liloRemoveUnexistentSections ("", "");
	BootCommon::liloUpdateInitrd ();
	BootCommon::liloUpdateAppend ();
	ret = BootCommon::Save () && ret;
        SCR::Execute (.target.bash, "/bin/chmod 600 /etc/lilo.conf");
        return ret;
    }


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

	if ( getBoardType () == "iseries" )
	{
	    ret = iSeriesSummary ();
	}
	else if ( getBoardType () == "prep" )
	{
	    ret = PRePSummary ();
	}
	else if ( getBoardType () == "chrp" )
	{
	    ret = CHRPSummary ();
	}
	// TODO modularize the rest when support exists
	else if ( getBoardType () == "mac_new" )
	{
	    ret = [
		// summary part
		_("Boot configuration on new PowerMacs is not yet implemented.")
	    ];
	}
	else if ( getBoardType () == "mac_old" )
	{
	    ret = [
		// summary part
		_("Configure the BootX App in classic MacOS to boot the file 'vmlinuz-oldworld'")
	    ];
	}
	else if ( getBoardType () == "mac_nubus" )
	{
	    // summary part
	    ret = [ _("Use the Apple MkLinux Booter to boot the kernel.")];
	}
	else
	{
	    ret = [
		// summary part
		 _("Boot configuration for this board is not yet implemented.")
	    ];;
	}
	// part of bootloader summary
	ret = prepend (ret, sformat (_("Board Type: %1"),
	    getNameOfCurrentBoard ()));
	y2milestone("BootPPC::Summary %1", ret);
	return ret;
    }

    /**
      * Update read settings to new version of configuration files
      */
    global define void Update () ``{
	currentBoardUpdate ();
	updateSections (true);
	updateGlobals ();
    }

    /**
      * Write bootloader settings to disk
      * @return boolean true on success
      */
    global define boolean Write () ``{
	boolean ret = true;
	if (!( getBoardType () == "prep" || getBoardType () == "chrp"
	    || getBoardType () == "mac_new" || getBoardType () == "iseries" ))
	{
            return true;
	}

	boolean requires_initrd
	    = SCR::Read (.target.size, "/boot/initrd") != -1;

	boot_device = BootCommon::BootPartitionDevice;
	root_device = BootCommon::RootPartitionDevice;

        if (getBoardType () == "iseries")
        {
	    iSeriesWrite (requires_initrd);
        }
        else if (getBoardType () == "prep")
        {
	    PRePWrite (requires_initrd);
        }
	else if (getBoardType () == "chrp")
	{
	    CHRPWrite (requires_initrd);
	}
	// TODO other subarchitectures

        if (BootCommon::activate)
	{
	    string bl_command = "/sbin/lilo "
		+ " >> /var/log/YaST2/y2loglilo 2>&1";
	    string bl_logfile = "/var/log/YaST2/y2loglilo";
	    ret = BootCommon::installBootLoader (bl_command, bl_logfile) && ret;
	}

	string post_command = "/usr/lib/YaST2/bin/ppc_postinstall.sh "
	    + boot_device + " " + root_device
	    + " >> /var/log/YaST2/y2logppcpostinst 2>&1";
	string post_log = "/var/log/YaST2/y2logppcpostinst";

	ret = BootCommon::installBootLoader (post_command, post_log) && ret;

        return ret;
    }

    /**
      * Initializer of the bootloader
      */
    global define void Initializer () ``{
	y2milestone ("Called PPC initializer");
	BootCommon::current_bootloader_attribs = $[
            "section_title" : "label",
            "kernel_key" : "image",
            "default_key" : "default",
            "default_type" : `label,
            "timeout_multiplier" : 10,
            "alias_keys" : ["alias"],
	    "convert_function" : BootCommon::liloConvertSection,
            "kernel_params" : $[
                "vga" : "vga",
                "root" : "root",
                `other : "append",
            ],
            "propose" : true,
            "read" : true,
            "scratch" : true,
            "restore_mbr" : true,
            "key_only_once" : true,
            "bootloader_on_disk" : true,
	    "option_list" : GetOptions (),
	];
	BootCommon::cwm_options = (map<string, map<string,any> >)union (
	    BootCommon::GetOptionMaps (),
	    BootPPC::GetOptionMaps ());
	BootCommon::cwm_widgets = (map<string, map<string,any> >)union (
	    BootCommon::GetWidgetMaps (),
	    BootPPC::GetWidgetMaps ());
    }

    /**
      * Return map of provided functions
      * @return map map of functions (eg. $["write":``(BootPPC::Write ())])
      */
    global define map<string, any> GetFunctions () ``{
	return $[
            "export"	: BootPPC::Export,
	    "import"	: BootPPC::Import,
	    "read"	: BootPPC::Read,
	    "reset"	: BootPPC::Reset,
	    "propose"	: BootPPC::Propose,
	    "save"	: BootPPC::Save,
	    "summary"	: BootPPC::Summary,
	    "update"	: BootPPC::Update,
	    "write"	: BootPPC::Write,
	    "widgets"	: BootPPC::GetWidgetMaps,
	    "getfiles"	: BootPPC::GetFiles,
	    "setfiles"	: BootPPC::SetFiles,
	];
    }

    /**
      * Constructor
      */
    global define void BootPPC () ``{
        BootCommon::bootloader_attribs["ppc"] = $[
            "required_packages" : [],// FIXME
            "loader_name" : "yaboot",
            "initializer" : BootPPC::Initializer,
        ];
    }

}
