/* * Command line interface for One Click Install */ { textdomain "OneClickInstall"; import "OneClickInstall"; import "CommandLine"; import "HTTP"; import "FTP"; string SEPARATOR = "/"; /* * Begin section that should be shared with OneClickInstallWorker. * Refactor before committing to SVN. * Duplicating here so people can use it on 10.3. */ boolean FuzzyMatch(string one, string two) { string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; return (tolower(filterchars(one,chars)) == tolower(filterchars(two,chars))); } /** ** Check whether this repository is already subscribed **/ list DeDupe(list url_list) { list sources = Pkg::SourceStartCache(true); list deduped = []; foreach (string new, url_list, { boolean dupeFound = false; foreach (integer srcid, sources, { map repoData = Pkg::SourceGeneralData(srcid); if (repoData["url"]:"" == new) { dupeFound = true; break; } if (FuzzyMatch(repoData["name"]:"",OneClickInstall::GetRepositoryName(new))) { dupeFound = true; break; } if (FuzzyMatch(repoData["alias"]:"",OneClickInstall::GetRepositoryName(new))) { dupeFound = true; break; } }); if (!dupeFound) deduped = add(deduped,new); }); return deduped; } list sourceids = []; /** ** Subscribe to all the specified repositories ** return true if all catalogues were added successfully, false otherwise. **/ boolean addRepositories(list repositories) { CommandLine::Print(_("Loading Package Management...")); list dedupedRepos = DeDupe(repositories); foreach (string new_url, dedupedRepos, { CommandLine::Print(sformat(_("Adding repository %1"),new_url)); boolean again = true; while(again) { map repoData = $[ "enabled":true, "autorefresh":true, "name":OneClickInstall::GetRepositoryName(new_url), "alias":OneClickInstall::GetRepositoryName(new_url), "base_urls":[new_url] ]; integer srcid = Pkg::RepositoryAdd(repoData); boolean success = Pkg::SourceRefreshNow(srcid); if (!success) { return false; } else { sourceids = add(sourceids,srcid); // save the repository Pkg::SourceSaveAll(); again = false; } } }); return true; } /** ** Install all the specified packages ** return true if all installations were successful, false otherwise **/ boolean installPackages(list packages) { Pkg::SourceLoad(); foreach (string name, packages, { CommandLine::Print(sformat(_("Marking package %1 for installation"),name)); if (!Pkg::PkgInstall(name)) CommandLine::Print(sformat(_("Warning: package %1 could not be installed."),name)); }); boolean state = true; Pkg::TargetInit( "/", false ); if(Pkg::PkgSolve(true)) { CommandLine::Print(_("Performing Installation...")); state = !(Pkg::PkgCommit(0)[0]:-1 < 0); } else //xxx no callback for resolve failures { symbol result = (symbol)WFM::CallFunction( "inst_packages", [`summaryMode]); if (result == `accept) { state = !(Pkg::PkgCommit(0)[0]:-1 < 0); } else { state = false; } } return state; } /** ** Install all the specified patterns ** return true if all installations were successful, false otherwise **/ boolean installPatterns(list patterns) { Pkg::TargetInit( "/", false ); foreach (string name, patterns, { if (!Pkg::ResolvableInstall(name,`pattern)) CommandLine::Print(sformat(_("Warning: pattern %1 could not be installed."),name)); }); boolean state = true; if(Pkg::PkgSolve(true)) { state = !(Pkg::PkgCommit(0)[0]:-1 < 0); } else //xxx no callback for resolve failures { symbol result = (symbol)WFM::CallFunction( "inst_packages", [`summaryMode]); if (result == `accept) { state = !(Pkg::PkgCommit(0)[0]:-1 < 0); } else { state = false; } } return state; } /** ** Remove all the specified packages ** return true if all installations were successful, false otherwise **/ boolean removePackages(list packages) { Pkg::TargetInit( "/", false ); boolean result = true; foreach (string name, packages, { result = Pkg::PkgDelete(name); }); boolean state = true; if(Pkg::PkgSolve(true)) { state = !(Pkg::PkgCommit(0)[0]:-1 < 0); } else //xxx no callback for resolve failures { symbol result = (symbol)WFM::CallFunction( "inst_packages", [`summaryMode]); if (result == `accept) { state = !(Pkg::PkgCommit(0)[0]:-1 < 0); } else { state = false; } } return state; } boolean removeAddedRepositories() { boolean success = true; foreach(integer srcid, sourceids, { success = success && Pkg::SourceDelete(srcid); } ); Pkg::SourceSaveAll(); return success; } string GrabFile(string url) { string newUrl = (string)SCR::Read (.target.tmpdir) + SEPARATOR + "metapackage.xml"; if (substring(url,0,4) == "http" || substring(url,0,4) == "file") { map response = HTTP::Get(url,newUrl); if (response["code"]:400 >= 400) return nil; return newUrl; } else if (substring(url,0,3) == "ftp") { FTP::Get(url,newUrl); return newUrl; } else { y2error ("Argument is neither local absolute path nor an HTTP or FTP URL. Bye."); return nil; } return nil; } /* * End section that should be shared with OneClickInstallWorker. */ boolean PrepareInstall(string ympFile, string tempFile) { OneClickInstall::Load(ympFile); if (!OneClickInstall::HaveAnythingToDo()) { y2error("Nothing to do specified in the YMP file"); CommandLine::Print(_("Error: Nothing to do specified in the YMP file")); return false; } if (OneClickInstall::HaveRepositories()) { CommandLine::Print(_("If you choose to continue, the following repositories will be subscribed to:")); foreach (string repository, OneClickInstall::GetRequiredRepositories(), { CommandLine::Print("\t* " + repository); }); } if (OneClickInstall::HaveSoftware()) { CommandLine::Print(_("If you choose to continue, the following software packages will be installed:")); foreach (string software, OneClickInstall::GetRequiredSoftware(), { CommandLine::Print("\t* " + software); }); } OneClickInstall::ToXML(tempFile); return true; } boolean PrepareInstallHandler(map options) { //trick ncurses string url = options["url"]:""; string tempFile = options["targetfile"]:""; if (substring(url,0,1) != "/") url = GrabFile(url); if (url == nil) { y2error ("Unable to retrieve YMP at %1",options["url"]:""); CommandLine::Print(sformat(_("Unable to retrieve YMP at %1"),options["url"]:"")); return false; } return PrepareInstall(url,tempFile); } boolean DoInstall(string xmlfile) { OneClickInstall::FromXML(xmlfile); if (OneClickInstall::HaveRepositoriesToInstall()) CommandLine::Print(_("Adding Repositories...")); boolean success = addRepositories(OneClickInstall::GetRequiredRepositories()); if (!success) { y2error("Unable to add repositories"); CommandLine::Print(_("Error: Unable to add respositories")); return false; } //Remove any removals if (OneClickInstall::HaveRemovalsToInstall()) { CommandLine::Print(_("Removing Packages...")); success = removePackages(OneClickInstall::GetRequiredRemoveSoftware()); } if (!success) { y2error("Unable to remove packages"); CommandLine::Print(_("Error: Unable to remove packages")); return false; } //if that was successful now try and install the patterns if (OneClickInstall::HavePatternsToInstall()) { CommandLine::Print(_("Installing Patterns...")); success = installPatterns(OneClickInstall::GetRequiredPatterns()); } if (!success) { y2error("Unable to install patterns"); CommandLine::Print(_("Error: Unable to install patterns")); return false; } //if that was successful now try and install the packages if (OneClickInstall::HavePackagesToInstall()) { CommandLine::Print(_("Installing Packages...")); success = installPackages(OneClickInstall::GetRequiredPackages()); } if (!success) { y2error("Unable to install packages"); CommandLine::Print(_("Error: Unable to install packages")); return false; } //If we don't want to remain subscribed, remove the repositories that were added for installation. if (OneClickInstall::HaveRepositoriesToInstall() && !OneClickInstall::GetRemainSubscribed()) { success = removeAddedRepositories(); } if (!success) { y2error("Unable to remove temporarily added repositories"); CommandLine::Print(_("Warning: Unable to remove temporarily added repositories.")); return false; } CommandLine::Print(_("Finished")); return true; } boolean AmRoot() { map out = (map) SCR::Execute (.target.bash_output, "/usr/bin/id --user"); return out["stdout"]:"" == "0\n"; } boolean DoInstallHandler(map options) { if ( AmRoot() ) return DoInstall(options["instructionsfile"]:""); else { y2error("Cannot install software as limited user"); CommandLine::Print(_("Error: Must be root")); return false; } } map cmdline = $[ "help" : _("One Click Install Command Line Installer"), "id" : "OneClickInstall", "actions" : $[ "prepareinstall" : $[ "help" : _("Processes a YMP file, ready for installation"), "handler" : PrepareInstallHandler ], "doinstall" : $[ "help" : _("Processes a YMP file, ready for installation"), "handler" : DoInstallHandler ] ], "options" : $[ "url" : $[ "help" : _("URL of .ymp file"), "type" : "string" ], "targetfile" : $[ "help" : _("File to put internal representation of YMP into"), "type" : "string" ], "instructionsfile" : $[ "help" : _("File containing internal representation of One Click Install instructions"), "type" : "string" ] ], "mappings" : $[ "prepareinstall" : [ "url" , "targetfile" ], "doinstall" : [ "instructionsfile" ] ] ]; any ret = CommandLine::Run(cmdline); return ret; }