ACC SHELL

Path : /usr/share/YaST2/clients/
File Upload :
Current File : //usr/share/YaST2/clients/sw_single.ycp

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