ACC SHELL

Path : /usr/share/YaST2/include/update/
File Upload :
Current File : //usr/share/YaST2/include/update/rootpart.ycp

/**
 * Module:	include/installation/rootpart.ycp
 *
 * Authors:	Stefan Schubert <schubi@suse.de>
 *		Arvin Schnell <arvin@suse.de>
 *              Jiri Srain <jsrain@suse.cz>
 *
 * Purpose:	Select root partition for update or booting.
 *		RootPart::rootPartitions must be filled before
 *		calling this module.
 */
{
textdomain "update";

import "Wizard";
import "Popup";
import "Label";
import "RootPart";
import "GetInstArgs";
import "Report";
import "Update";
import "Installation";
import "FileSystems";

// Returns boolean whether partition can be
// a Linux 'root' file system
boolean CanBeLinuxRootFS (symbol partition_fs) {
    if (partition_fs == nil) {
	y2error("partition_fs not defined!");
	return false;
    }

    // possible_root_fs contains list of supported FSs
    return contains (FileSystems::possible_root_fs, partition_fs);
}

// flavor is either `update or `boot
list <term> make_partition_list (boolean withall, symbol flavor) {
	list<term> part_list = [];
	foreach (string partition, map i, RootPart::rootPartitions, {
	    // see https://bugzilla.novell.com/attachment.cgi?id=96783&action=view

	    // see bugzilla #288201
	    // architecture needs to be valid when updating, not booting
	    boolean arch_is_valid = (flavor == `boot ? true:i[`arch_valid]:false);

	    if (withall || (i[`valid]:false && arch_is_valid))
	    {
		// `ext2, `jfs, ...
		symbol part_fs = (symbol) i[`fs]:nil;
		string part_fs_name = tostring (part_fs);
		if (part_fs_name != nil && regexpmatch (part_fs_name, "^`(.*)$")) {
		    part_fs_name = regexpsub (part_fs_name, "^`(.*)$", "\\1");
		}

		string system = i[`name]:"error";
		// unknown system
		if (system == "unknown") {
		    if (part_fs != nil) {
			if (CanBeLinuxRootFS (part_fs)) {
			    // Table item (unknown system)
			    system = _("Unknown Linux");
			} else {
			    // Table item (unknown system)
			    system = _("Unknown or Non-Linux");
			}
		    } else {
			// Table item (unknown system [neither openSUSE 11.1 nor SLES 14 nor ...])
			if (system == "unknown") system = _("Unknown");
		    }
		}

		string arch = i[`arch]:"error";
		// Table item (unknown architecture)
		if (arch == "unknown") arch = _("Unknown");

		// fist, use the name of file system (with short name for Linux)
		// then the file system short name
		// then "Unknown"
		string fs = "";

		// is a linux fs, can be a root fs, has a fs name
		if (part_fs != nil && i[`fstype]:nil != nil && CanBeLinuxRootFS (part_fs) && part_fs_name != nil) {
		    fs = sformat(
			_("%1 (%2)"),
			i[`fstype]:"", part_fs_name
		    );
		} else {
		    fs = i[`fstype]:i[`fs]:"";
		}
		// Table item (unknown file system)
		if (fs == "") fs = _("Unknown");
		
		string label = i[`label]:"";

		part_list = add (part_list,
		    `item (`id (partition),
			system, partition, arch, fs, label
		    )
		);
	    }
	});
	return part_list;
}

// Returns whether wanted and selected architectures match
// bnc #372309
boolean DoArchitecturesMatch (string arch_1, string arch_2) {
    list <string> ppc_archs = ["ppc", "ppc64"];

    // exact match
    if (arch_1 == arch_2) {
	return true;
    // ppc exception
    } else if (contains (ppc_archs, arch_1) && contains (ppc_archs, arch_2)) {
	return true;
    }

    // else
    return false;
}

void UmountMountedPartition () {
    Update::Detach ();
    RootPart::UnmountPartitions (false);
}

/**
 * This dialog comes in several different flavors:
 * `update_dialog - used to show partitions available for upgrade,
 * `update_popup - obsolete, used to be used as a pop-up in proposal dialog (MakeProposal),
 * `update_dialog_proposal - obsolete, used to be used as a pop-up in proposal dialog (AskUser),
 * `boot_popup - obsolete, used to be used as a dilaog offering to boot to a selected partition,
 *
 * @param symbol flavor
 * @return symbol `cancel, `back, `next, `abort
 */
symbol RootPartitionDialog (symbol flavor) {
    // FIXME: Most of the code in this function is obsolete

    list<term> partition_list = make_partition_list (
	RootPart::showAllPartitions,
	(flavor == `boot_popup ? `boot : `update)
    );

    string title = "";
    string label = "";
    string help_text = "";

    if (flavor == `boot_popup)
    {
	// label for selection of root partition (for boot)
	label = _("Partition or System to Boot:");

	// help text for root partition dialog (for boot)
	help_text = _("<p>
Select the partition or system to boot.
</p>
");
    }
    else
    {
	// label for selection of root partition (for update)
	label = _("Partition or System to Update:");

	// help text for root partition dialog (for update)
	help_text = _("<p>
Select the partition or system to update.
</p>
");

	if (flavor == `update_dialog || flavor == `update_dialog_proposal)
	{
	    // headline for dialog "Select for update"
	    title = _("Select for Update");
	}
    }

    // help text for root partition dialog (general part)
    help_text = help_text + _("<p>
<b>Show All Partitions</b> expands the list to a
general overview of your system's partitions.
</p>
");

    term contents = `HBox (
	`VBox (
	    `VSpacing (1),
	    `Left (`Label (label)),
	    `MinSize (
		70, 14,
		`Table (
		    `id (`partition),
		    `opt(`hstretch),
		    `header (
			// table header
			_("System"),
		        // table header item
			_("Partition"),
		        // table header item
			_("Architecture"),
			// table header item
			_("File System"),
			// table header item
			_("Label")
		    ),
		    partition_list
		)
	    ),
	    `Left(`CheckBox(`id(`showall), `opt(`notify),
		// check box
		_("&Show All Partitions"), false)),
	    `VSpacing (1)
	)
    );

    // bnc #429080
    // finishing the target before selecting a new system to load
    if (flavor == `update_dialog) {
	Pkg::TargetFinish();
    }

    if (flavor == `update_dialog)
    {
	Wizard::SetContents (title, contents, help_text, true, true);
    }
    else if (flavor == `update_dialog_proposal)
    {
	Wizard::CreateDialog ();
	Wizard::SetContentsButtons (title, contents, help_text,
	    Label::BackButton (), Label::OKButton ());
    }
    else
    {
	term buttons = `PushButton (`id(`next), `opt(`default), Label::OKButton());

	if (flavor == `boot_popup)
	{
	    buttons = `HBox (
			     `HStretch (),
			     // pushbutton to (rightaway) boot the system selected above
			     `HWeight( 1, `PushButton(`id(`next), `opt(`default), _("&Boot")) ),
			     `HSpacing( 1 ),
			     `HWeight( 1, `PushButton(`id(`cancel), Label::CancelButton() ) ),
			     `HStretch ()
			     );
	}

	term full = `MinHeight (
	    16,
		`HBox (
		    `HSquash (
			`MinWidth (
			    26,
			    `RichText (`opt(`vstretch), help_text)
			)
		    ),
		    `HSpacing (2),
		    `VBox (
			`MinHeight (
			    15,
			    contents
			),
			buttons
		    ),
		    `HSpacing (2)
		)
	);

	UI::OpenDialog (full);
    }


    if (size (RootPart::selectedRootPartition) > 0)
	UI::ChangeWidget (`id(`partition), `CurrentItem, RootPart::selectedRootPartition);

    UI::ChangeWidget(`id(`showall), `Value, RootPart::showAllPartitions);


    any ret = nil;

    while (true)
    {
	if (flavor == `update_dialog || flavor == `update_dialog_proposal)
	    ret = Wizard::UserInput ();
	else
	    ret = UI::UserInput ();

	if (ret == `abort && Popup::ConfirmAbort (`painless))
	    break;

	if (ret == `showall)
	{
	    string tmp = (string) UI::QueryWidget (`id(`partition), `CurrentItem);
	    partition_list = make_partition_list (
		(boolean) UI::QueryWidget (`id(`showall), `Value),
		(flavor == `boot_popup ? `boot : `update)
	    );
	    UI::ChangeWidget (`id(`partition), `Items, partition_list);
	    if (tmp != nil)
		UI::ChangeWidget (`id(`partition), `CurrentItem, tmp);
	    continue;
	}
	if ((flavor == `update_dialog || flavor == `update_popup
	    || flavor == `update_dialog_proposal)
	    && ret == `next)
	{
	    string selected = (string) UI::QueryWidget (`id(`partition), `CurrentItem);
	    map freshman = RootPart::rootPartitions[selected]:$[];
	    boolean cont = true;
	    y2milestone ("Selected root partition: %1 %2",
		selected, freshman);
	    if (freshman[`name]:"unknown" == "unknown")
	    {
		cont = Popup::ContinueCancel (
// continue-cancel popup
_("No installed system that can be upgraded with this product was found
on the selected partition."));
	    }
	    else if (! DoArchitecturesMatch (freshman[`arch]:"", RootPart::GetDistroArch()))
	    {
		cont = Popup::ContinueCancel (
// continue-cancel popup
_("The architecture of the system installed in the selected partition
is different than the one of this product."));

	    }
	    if (! cont)
		ret = nil;
	}
	if (ret == `next)
	{
	    RootPart::selectedRootPartition = (string) UI::QueryWidget (`id(`partition), `CurrentItem);
	    RootPart::showAllPartitions = (boolean) UI::QueryWidget (`id(`showall), `Value);

	    if (flavor == `update_dialog)
	    {
		RootPart::targetOk = RootPart::mount_target ();

		// Not mounted correctly
		if (! RootPart::targetOk) {
		    // error report
		    Report::Error (_("Failed to mount target system"));
		    UmountMountedPartition();
		    continue;

		// Correctly mounted but incomplete installation found
		} else if (RootPart::IncompleteInstallationDetected (Installation::destdir)) {
		    if (Popup::AnyQuestion (
			Label::WarningMsg(),
			// pop-up question
			_("A possibly incomplete installation has been detected on the selected partition.
Are sure you want to use it anyway?"),
			// button label
			_("&Yes, Use It"),
			Label::CancelButton(),
			`focus_no
		    )) {
			y2milestone ("User wants to update possibly incomplete system");
		    } else {
			y2milestone ("User decided not to update incomplete system");
			UmountMountedPartition();
			continue;
		    }
		}
	    }
	    break;
	}
	if (ret == `cancel || ret == `back || ret == `next)
	    break;
    }

    if (flavor != `update_dialog)
	UI::CloseDialog ();

    // New partition has been mounted
    if (flavor == `update_dialog && ret == `next) {
	// Target load failed, #466803
	if (Pkg::TargetInitialize (Installation::destdir) != true) {
	    y2error ("Pkg::TargetInitialize failed");
	    if (Popup::AnyQuestion (
		Label::ErrorMsg(),
		_("Initializing the system for upgrade has failed for unknown reason.
It is highly recommended not to continue the upgrade process.

Are you sure you want to continue?"),
		_("&Yes, Continue"),
		Label::CancelButton(),
		`focus_no
	    )) {
		ret = `back;
	    } else {
		y2warning ("User decided to continue despite the error above (Pkg::TargetInit() failed)");
	    }
	}

	// not aborted
	if (ret != `back) {
	    // Target load failed, #466803
	    if (Pkg::TargetLoad() != true) {
		y2error ("Pkg::TargetLoad failed");
		if (Popup::AnyQuestion (
		    Label::ErrorMsg(),
		    _("Initializing the system for upgrade has failed for unknown reason.
It is highly recommended not to continue the upgrade process.

Are you sure you want to continue?"),
		    _("&Yes, Continue"),
		    Label::CancelButton(),
		    `focus_no
		)) {
		    ret = `back;
		} else {
		    y2warning ("User decided to continue despite the error above (Pkg::TargetLoad() failed)");
		}
	    }
	}
    }

    return (symbol)ret;
}

} // EOF

ACC SHELL 2018