ACC SHELL
/**
* Module: sw_single.ycp
*
* Authors: Gabriele Strattner (gs@suse.de)
* Klaus Kaempf <kkaempf@suse.de>
*
* Purpose: contains dialog loop for workflows:
* "Install/Remove software"
*
* $Id: sw_single.ycp 61741 2010-04-19 07:30:43Z lslezak $
*
* Note: sw_single accepts a map parameter: $[ "dialog_type" : symbol,
* "repo_mgmt" : boolean ]
*
* "dialog_type" can be `patternSelector, `searchMode, `summaryMode
* "repo_mgmt" enables "Repositories" -> "Repository Manager..." menu option
*/
{
textdomain "packager";
import "Confirm";
import "Installation";
import "Mode";
import "PackageCallbacks";
import "PackageLock";
import "PackageSlideShow";
import "SlideShow";
import "SlideShowCallbacks";
import "Kernel";
import "Wizard";
import "Popup";
import "GetInstArgs";
import "PackageSystem";
import "Report";
import "FileUtils";
import "PackagesUI";
import "CommandLine";
import "Progress";
import "Directory";
import "String";
import "URL";
boolean force_summary = false;
symbol StartSWSingle()
{
Wizard::CreateDialog();
Wizard::SetDesktopIcon("sw_single");
// a stage in the progress dialog
list<string> stages = [_("Initialize the Target System"), _("Load the Configured Repositories")];
// an extra step is needed in dir inst mode
if (Installation::destdir != "/")
{
y2internal("Extra step is needed");
// %1 is path to the target system (e.g. /tmp/dirinstall
sformat(_("Reset the target system to %1"), Installation::destdir);
}
// a stage in the progress dialog
Progress::New(_("Starting the Software Manager"), "", 2, stages, [], "");
Progress::NextStage();
import "Packages";
list<string> packagelist = []; // list of packages to act on
// `install
// `remove, `update: used from gnome-main-menu (#222757)
symbol action = `install;
boolean test_popup = false;
boolean skip_source = false;
// =============================================================
// check test_popup
// test_mode is checked for in Installation constructor
define list<string> CheckArguments ()
{
integer arg_n = size (WFM::Args()) - 1;
list<string> arg_list = [];
while (arg_n >= 0)
{
if (WFM::Args(arg_n) == .test)
{
Mode::SetTest ("test");
}
else if (WFM::Args(arg_n) == .testp)
{
Mode::SetTest ("test"); // .testp implies .test
test_popup = true;
}
else if (is (WFM::Args(arg_n), string))
{
string s = tostring (WFM::Args(arg_n));
if (s == "--install")
action = `install;
else if (s == "--remove")
action = `remove;
else if (s == "--update")
action = `update;
else
arg_list = add (arg_list, s);
}
else if (is (WFM::Args(arg_n), list))
{
foreach (any arg, (list)WFM::Args(arg_n), ``{ arg_list = add (arg_list, tostring (arg));});
}
arg_n = arg_n - 1;
}
y2milestone ("action: %1", action);
return arg_list;
}; // CheckArguments
//
// CheckWhichPackages
//
// Check arg_list:
// If we're called with an absolute package path just install
// this package without paying attention to dependencies.
//
// returns `done all done
// `failed package not found
// `found_descr started package manager
//
define symbol CheckWhichPackages (list<string> arg_list)
{
if (!Pkg::TargetInit("/", false))
{
// error message
Report::Error("Cannot read the list of installed packages.");
return `failed;
}
y2milestone ("CheckWhichPackages (%1)", arg_list);
// if sw_single is called with a list of packages or a package name
string first_arg = "";
if (size (arg_list) > 0)
{
first_arg = arg_list[0]:"";
}
/*
* If the first argument is a package ending with .rpm call Pkg::TargetInstall for
* each arg.
*/
if (regexpmatch (first_arg, "\\.rpm$")) // package name given
{
PackageSystem::EnsureSourceInit();
// if sw_single is called with an absolute package-pathname, there is no need to
// mount the source medium or check SuSE version or dependencies
PackageSlideShow::InitPkgData(true); // force reinitialization
// create a temporary Plaindir repository
string tmpdir = (string) SCR::Read(.target.tmpdir);
string tmprepo = tmpdir + "/tmp_install_repo";
// create mount point directory
SCR::Execute(.target.mkdir, tmprepo);
foreach (string package, arg_list, {
// a symbolic link
string command = sformat("ln -- '%1' '%2'", String::Quote(package), String::Quote(tmprepo));
y2milestone("Linking package using command: %1", command);
map out = (map)SCR::Execute(.target.bash_output, command);
if (out["exit"]:-1 != 0)
{
y2warning("Could not link the package, creating a full copy instead...");
command = sformat("cp -- '%1' '%2'", String::Quote(package), String::Quote(tmprepo));
y2milestone("Copying package using command: %1", command);
out = (map)SCR::Execute(.target.bash_output, command);
if (out["exit"]:-1 != 0)
{
// error message (%1 is a package file name)
Report::Error(sformat(_("Error: Cannot copy package %1 to temporary repository."), package));
return `failed;
}
}
});
string url = URL::Build($["scheme" : "file", "path" : tmprepo]);
y2milestone("Using tmp repo URL: %1", url);
integer repo_id = nil;
if (url != "")
{
repo_id = Pkg::SourceCreateType(url, "", "Plaindir");
y2milestone("Adde temporary repository with ID %1", repo_id);
if (repo_id == nil)
{
// error message
Report::Error(sformat(_("Error: Cannot add a temporary directory, packages cannot be installed.")));
return `failed;
}
}
else
{
return `failed;
}
foreach (string package, arg_list, {
if (SCR::Read (.target.size, package) > 0)
{
map out = (map)SCR::Execute(.target.bash_output,
sformat("/bin/rpm -q --qf '%%{NAME}' -p '%1'", String::Quote(package)));
if (out["exit"]:-1 != 0)
{
// error message
Report::Error(sformat(_("Error: Cannot query package file %1."), package));
return `failed;
}
string package_name = out["stdout"]:"";
y2milestone ("Installing %1 from file %2 (repository %3)", package_name, package, repo_id);
boolean installed = Pkg::ResolvableInstallRepo(package_name, `package, repo_id);
if (!installed)
{
// Error message:
// %1 = package name (may include complete RPM file name)
// %2 = error message
Report::Error(sformat(_("Package %1 could not be installed.
Details:
%2
"), package, Pkg::LastError()));
}
}
else
{
// error popup, %1 is the name of the .rpm package
string message = sformat (_("Package %1 was not found on the medium."), package);
y2error ("SW_SINGLE: Package %1 was not found on the medium", package);
Popup::Message (message);
return `failed;
}
});
Pkg::PkgSolve(false);
force_summary = true;
}
else if (first_arg != "") // firstarg given, but not *.rpm
{
string arg_name = arg_list[0]:"";
if ( ! FileUtils::IsFile(arg_name) || FileUtils::GetSize(arg_name) <= 0 ) // Check: a local file ? bigger than 0?
{
packagelist = arg_list; // No: expect package names
}
else // Yes: try to read the file
{
y2milestone("Reading file %1", arg_name );
packagelist = (list<string>) SCR::Read( .target.ycp, arg_name); // try .ycp list first
if ((packagelist == nil)
|| (packagelist == []))
{
string packagestr = (string) SCR::Read( .target.string, arg_name); // string ascii file next
packagelist = splitstring (packagestr, "\n");
// filter empty lines out, bug #158226
packagelist = filter (string package, packagelist, { return !regexpmatch(package, "^ *$"); });
}
}
y2milestone("packagelist: %1", packagelist );
}
// start package manager
const boolean enabled_only = true;
Progress::NextStage();
boolean mgr_ok = Pkg::SourceStartManager(enabled_only);
if (!mgr_ok)
{
Report::LongWarning(_("There was an error in the repository initialization.") + "\n" + Pkg::LastError ());
}
if (size(Pkg::SourceGetCurrent(enabled_only)) == 0)
{
Report::Warning(_("No repository is defined.
Only installed packages are displayed."));
}
// reset the target if needed (e.g. dirinstall mode)
// EnsureTargetInit() uses "/" as root
if (Installation::destdir != "/")
{
y2milestone("Setting a new target: %1", Installation::destdir);
Progress::NextStage();
Pkg::TargetInit (Installation::destdir, false);
}
return `found_descr;
}; // CheckWhichPackages
// originally stolen from inst_do_net_test.ycp:IsDownloadedVersionNewer
/**
* Function checks two versions of installed rpm and decides
* whether the second one is newer than the first one. This
* function ignores non-numerical values in versions.
* Version and Release parts are merged!
* FIXME make a binding to librpm.
* @param string first version
* @param string second version
* @return boolean true if the second one is newer than the first one
*/
boolean VersionALtB (string a_version, string b_version) {
list <string> a_version_l = filter(string s, splitstring(a_version, "-\."), {
return regexpmatch(s, "^[0123456789]+$");
});
list <string> b_version_l = filter(string s, splitstring(b_version, "-\."), {
return regexpmatch(s, "^[0123456789]+$");
});
y2milestone("Comparing versions %1 and %2", a_version_l, b_version_l);
integer a_size = size (a_version_l);
integer b_size = size (b_version_l);
integer longer_size = a_size > b_size? a_size: b_size;
boolean b_version_is_newer = false;
integer compare = 0; // <0 if a<b, =0 if a==b, >0 if a>b
integer i = 0;
while (i < longer_size) {
// -1 will make the desirable outcome of "2" < "2.0"
integer a_item = tointeger(a_version_l[i]:"-1");
integer b_item = tointeger(b_version_l[i]:"-1");
if (a_item < b_item) {
compare = -1;
break;
}
if (a_item > b_item) {
compare = 1;
break;
}
i = i + 1;
};
y2milestone("%1 <=> %2 -> %3", a_version, b_version, compare);
return compare < 0;
}
/**
* Check if there is an uninstalled package of the same name with a
* higher version. Otherwise we would forcefully reinstall it. #222757#c9
*/
boolean CanBeUpdated (string package) {
list< map<string,any> > props = Pkg::ResolvableProperties (package, `package, "" /*any version*/);
// find maximum version and remember
// if it is installed
string max_ver = "0";
boolean max_is_installed = false;
foreach (map<string,any> prop, props, {
string cur_ver = prop["version"]:"0";
if (VersionALtB (max_ver, cur_ver)) {
max_ver = cur_ver;
// `installed or `selected is ok
max_is_installed = prop["status"]:`available != `available;
y2milestone ("new max: installed: %1", max_is_installed);
}
});
return !max_is_installed;
}
map<string, any> GetPackagerOptions()
{
// defaults
symbol mode = nil;
boolean repo_management = nil;
y2milestone("Args: %1", WFM::Args());
foreach(any a, WFM::Args(),
{
if (is(a, map))
{
map m = (map)a;
if (haskey(m, "dialog_type"))
{
mode = m["dialog_type"]:`searchMode;
}
if (haskey(m, "repo_mgmt"))
{
repo_management = m["repo_mgmt"]:false;
}
}
}
);
// use default parameters for missing or invalid values
if (mode == nil)
{
// use summary mode if there is something to install (probably a suggested or recommended package) (bnc#465194)
Pkg::PkgSolve(true); // select the packages
mode = (Pkg::IsAnyResolvable(`any, `to_install) || Pkg::IsAnyResolvable(`any, `to_remove)) ? `summaryMode : `searchMode;
}
if (repo_management == nil)
{
repo_management = Mode::normal();
}
map<string, any> ret = $[ "mode" : mode, "enable_repo_mgr" : repo_management ];
y2milestone("PackagesUI::RunPackageSelector() options: %1", ret);
return ret;
}
// =============================================================
// check whether running as root
// and having the packager for ourselves
if (! Confirm::MustBeRoot ())
{
UI::CloseDialog ();
return `abort;
}
if (!PackageLock::Connect(false)["connected"]:false)
{
// SW management is already in use, access denied
// the yast module cannot be started
UI::CloseDialog ();
return `abort;
}
// check Args
// set test_mode, test_popup
list<string> arg_list = CheckArguments();
// check the arguments and try the mount/search for local description
symbol result = CheckWhichPackages (arg_list);
Progress::Finish();
Pkg::SetTextLocale (UI::GetLanguage (true));
y2milestone ("SW_SINGLE: result CheckWhichPackages %1", result);
if ((result == `done)
|| (result == `failed))
{
UI::CloseDialog();
return `next;
}
boolean force_restart = false;
boolean found_descr = result == `found_descr;
do
{
// reset summary
PackagesUI::ResetPackageSummary();
force_restart = false;
list<string> old_failed_packs = [];
if ((integer)SCR::Read (.target.size,
"/var/lib/YaST2/failed_packages") > 0)
{
old_failed_packs = (list<string>)
SCR::Read (.target.ycp, "/var/lib/YaST2/failed_packages");
}
if (size (old_failed_packs) > 0 && Popup::YesNo (
_("During the last package installation,
several package failed to install.
Install them now?
")))
{
foreach (string p, old_failed_packs, {
Pkg::PkgInstall (p);
});
}
if ( found_descr )
{
if (size (packagelist) == 0) // packages given ?
{
// names of taboo packages
list<string> taboo_packages = Pkg::GetPackages(`taboo, true);
map<string,any> opts = GetPackagerOptions();
y2milestone("Using packager widget options: %1", opts);
result = PackagesUI::RunPackageSelector(opts); // No: ask user via package selection widget
y2milestone ("inst_packages returns %1", result);
if (result == `accept)
{
result = `next;
}
// start the repository manager
else if (result == `repo_mgr)
{
symbol repo_result = (symbol)WFM::CallFunction( "repositories", [ `sw_single_mode ]);
force_restart = true;
}
else if (result == `online_update_configuration)
{
symbol online_result = (symbol)WFM::CallFunction( "online_update_configuration", []);
force_restart = true;
}
else if (result == `webpin)
{
string required_package = "yast2-packager-webpin";
if (!PackageSystem::Installed( required_package ))
{
if ( !PackageSystem::CheckAndInstallPackages([required_package]) )
Report::Error( sformat(_("Cannot search packages in online repositories
without having package %1 installed"), required_package));
}
else
{
symbol webpin_result = (symbol)WFM::CallFunction( "webpin_package_search", []);
}
force_restart = true;
}
}
else
{
list<string>nonexisting = filter (string p, packagelist, {
return ! Pkg::IsAvailable (p);
});
if (action != `remove && size (nonexisting) > 0)
{
string missing = mergestring (nonexisting, ", ");
y2error ("Tags %1 aren't available",
mergestring (nonexisting, ", "));
Report::LongError (sformat (
// error report, %1 is a list of packages
_("Following packages haven't been found on the medium:\n%1"),
mergestring (nonexisting, "\n")));
return `cancel;
}
foreach (string package, packagelist, // Yes: install them
{
if (action == `install
// TODO `update: tell the user if already up to date
|| (action == `update && CanBeUpdated (package))) {
// select package for installation
if (!Pkg::PkgInstall (package)) {
// oops, package not found ? try capability
Pkg::DoProvide ([package]);
}
}
else if (action == `remove) {
if (!Pkg::PkgDelete (package)) {
// package failed, try capability
Pkg::DoRemove ([package]);
}
}
});
// confirm removal by user (bnc#399795)
if (action == `remove)
{
map opts = $[ "dialog_type" : `summaryMode, "repo_mgmt" : true ];
y2milestone("Using packager widget options: %1", opts);
result = (symbol)WFM::CallFunction( "inst_packages", [opts]);
if (result != `accept)
return `abort;
}
if (Pkg::PkgSolve (false)) // Solve dependencies
{
result = `next; // go-on if no conflicts
}
else
{
result = (symbol)WFM::CallFunction( "inst_packages", [ $[ "dialog_type": `summaryMode, "repo_mgmt" : true] ]); // Ask user if conflicts
y2milestone ("inst_packages returns %1", result);
if (result == `accept)
result = `next;
}
}
}
if (result == `next) // packages selected ?
{
// ask user to confirm all remaining licenses (#242298)
boolean licenses_accepted = PackagesUI::ConfirmLicenses();
// all licenses accepted?
if (!licenses_accepted)
{
// no, go back to the package selection
force_restart = true;
continue;
}
SCR::Write (.target.ycp, "/var/lib/YaST2/failed_packages", []);
boolean anyToDelete = Pkg::PkgAnyToDelete();
SlideShow::SetLanguage (UI::GetLanguage(true));
PackageSlideShow::InitPkgData(true); // force reinitialization
SlideShow::OpenDialog();
list< map<string,any> > stages = [
$[
"name" : "packages",
"description": _("Installing Packages..."),
"value" : PackageSlideShow::total_size_to_install / 1024 , // kilobytes
"units" : `kb,
],
];
SlideShow::Setup( stages );
SlideShow::MoveToStage( "packages" );
import "PackageInstallation";
integer oldvmlinuzsize = (integer) SCR::Read(.target.size, "/boot/vmlinuz");
list commit_result = PackageInstallation::CommitPackages (0, 0); // Y: commit them !
integer newvmlinuzsize = (integer) SCR::Read(.target.size, "/boot/vmlinuz");
SlideShow::CloseDialog();
if (Mode::normal () // run SuSEconfig only in normal system, not during installation
&& Installation::destdir == "/"
&& ((commit_result[0]:0 > 0)
|| anyToDelete))
{
// prepare "you must boot" popup in inst_suseconfig
Kernel::SetInformAboutKernelChange (oldvmlinuzsize != newvmlinuzsize);
string suseconfig_log = Directory::logdir + "/y2log.SuSEconfig";
integer suseconfig_log_size = FileUtils::GetSize(suseconfig_log);
if (suseconfig_log_size < 0)
{
suseconfig_log_size = 0;
}
result = (symbol) WFM::CallFunction ("inst_suseconfig", [GetInstArgs::Buttons(false, false)]);
// read only the new lines at the end
string command = sformat("dd bs=1 if='%1' skip=%2", String::Quote(suseconfig_log), suseconfig_log_size);
y2milestone("Reading new susconfig log: %1", command);
map out = (map)SCR::Execute(.target.bash_output, command);
if (out["exit"]:-1 == 0)
{
PackagesUI::SetPackageSummaryItem("postinstall_log", out["stdout"]:"");
}
else
{
y2error("Could not read susconfig log: %1", out);
}
}
if (Mode::normal())
{
string PKGMGR_ACTION_AT_EXIT = (string)SCR::Read(.sysconfig.yast2.PKGMGR_ACTION_AT_EXIT);
if (PKGMGR_ACTION_AT_EXIT == nil)
{
PKGMGR_ACTION_AT_EXIT = "close";
}
y2milestone("PKGMGR_ACTION_AT_EXIT: %1, force_summary: %2", PKGMGR_ACTION_AT_EXIT, force_summary);
// display installation summary if there has been an error
// or if it's enabled in sysconfig
if (PKGMGR_ACTION_AT_EXIT == "summary"
|| force_summary
|| size(commit_result[1]:[]) > 0)
{
if (PackagesUI::ShowInstallationSummary() == `back && size(packagelist) == 0)
{
force_restart = true;
}
}
else if (PKGMGR_ACTION_AT_EXIT == "restart" && size(packagelist) == 0)
{
force_restart = true;
}
}
}
} while ( force_restart );
UI::CloseDialog();
return (symbol) result;
}
/*
* Start commandline interface only when the parameter is "help", otherwise start standard GUI.
* The reason is that "yast2 -i package" is translated to "yast2 sw_single package",
* we don't know wheter "package" is a command or a package name.
* Package name is assumed for backward compatibility.
*/
if (WFM::Args() == ["help"])
{
map cmdline_description = $[
"id" : "sw_single",
/* Command line help text for the software management module, %1 is "zypper" */
"help" : sformat(_("Software installation - this module doesn't support the command line interface, use '%1' instead."), "zypper"),
"guihandler" : StartSWSingle,
];
return CommandLine::Run(cmdline_description);
}
else
{
return StartSWSingle();
}
}
ACC SHELL 2018