ACC SHELL
/**
* File: modules/SourceManager.ycp
* Package: Package Repository Management
* Summary: SourceManager settings, input and output functions
* Authors: Anas Nashif <nashif@suse.de>
* Lukas Ocilka <locilka@suse.cz>
* Martin Vidner <mvidner@suse.cz>
* Status: Work in Progress
*
* $Id: SourceManager.ycp 60257 2010-01-07 12:46:56Z lslezak $
*
* Representation of the configuration of source-manager.
* Input and output routines.
*/
{
textdomain "packager";
module "SourceManager";
import "Progress";
import "Report";
import "Popup";
import "Label";
import "Summary";
import "HTML";
import "Mode";
import "URL";
import "Linuxrc";
import "Installation";
import "String";
global list<integer> newSources = [];
global integer numSources = 0;
global list<integer> sourceStates = [];
global list<map<string,any> > sourceStatesIn = [];
global list<map<string,any> > sourceStatesOut = [];
global map url_tokens = $[];
global string currentUrl = "";
// Sources that are removed in memory but still not in libzypp
// They will be removed in Write() at the end
global list <integer> just_removed_sources = [];
/**
* Prototypes
*/
global boolean Modified();
global symbol createSource( string url );
/**
* Data was modified?
*/
global boolean modified = false;
/**
*/
global boolean proposal_valid = false;
/**
* Abort function
* return boolean return true if abort
*/
global boolean AbortFunction() {
return false;
}
/**
* Abort function
* @return boolean return true if abort
*/
global define boolean Abort() ``{
if(AbortFunction != nil)
{
return AbortFunction () == true;
}
return false;
}
/**
* Data was modified?
* @return true if modified
*/
global boolean Modified() {
y2debug("modified=%1",modified);
//return modified;
return (sourceStatesIn != sourceStatesOut);
}
global boolean ReadSources()
{
boolean success = Pkg::SourceStartManager( false );
if (!success)
return success;
sourceStates = Pkg::SourceStartCache ( false );
sourceStatesIn = Pkg::SourceEditGet();
sourceStatesOut = sourceStatesIn;
return true;
}
/**
* Read all source-manager settings
* @return true on success
*/
global boolean Read() {
/* SourceManager read dialog caption */
string caption = _("Initializing Available Repositories");
integer steps = 2;
// We do not set help text here, because it was set outside
Progress::New( caption, " ", steps, [
/* Progress stage 1/3 */
_("Read configured repositories"),
/* Progress stage 2/3 */
_("Detect available repositories via SLP")
], [
/* Progress step 1/3 */
_("Reading configured repositories..."),
/* Progress step 2/3 */
_("Detecting available repositories..."),
/* Progress finished */
_("Finished")
],
""
);
// read database
if(Abort()) return false;
Progress::NextStage();
/* Error message */
if(!ReadSources()) Report::Error(_("Cannot read repositories."));
// read another database
if(Abort()) return false;
Progress::NextStep();
/* Error message */
if(false) Report::Error(_("Cannot detect available repositories."));
if(Abort()) return false;
/* Progress finished */
Progress::NextStage();
if(Abort()) return false;
modified = false;
return true;
}
/**
* Commit changed repositories
*/
global boolean CommitSources() {
y2debug("In: %1 Out: %2", sourceStatesIn, sourceStatesOut );
boolean success = false;
while (true)
{
success = Pkg::SourceEditSet( sourceStatesOut );
if ( !success ) {
// popup message header
string _msg1 = _("Unable to save changes to the repository.
");
// popup message, after message header, header of details
string _msg2 = _("Details:") + "\n" + Pkg::LastError();
// end of popup message, question
_msg2 = _msg2 + "\n" + _("Try again?");
boolean tryagain = Popup::YesNo( _msg1 + "\n" + _msg2 );
if (tryagain )
continue;
else
break;
}
else
{
break;
}
}
return success;
}
/**
* Write all repository-manager settings
* @return true on success
*/
global boolean Write() {
/* SourceManager read dialog caption */
string caption = _("Saving Repository Configuration");
integer steps = 1;
// We do not set help text here, because it was set outside
Progress::New(caption, " ", steps, [
/* Progress stage 1/1 */
_("Write repository settings"),
], [
/* Progress step 1/1 */
_("Writing the settings..."),
/* Progress finished */
_("Finished")
],
""
);
// write settings
if(Abort()) return false;
Progress::NextStage();
/* Error message */
boolean exit = CommitSources();
if(Abort()) return false;
/* Progress finished */
Progress::NextStage();
if(Abort()) return false;
return exit;
}
/**
* Get all repository-manager settings from the first parameter
* (For use by autoinstallation.)
* @param settings The YCP structure to be imported.
* @return boolean True on success
*/
global boolean Import (map settings) {
return true;
}
/**
* Dump the repository-manager settings to a single map
* (For use by autoinstallation.)
* @return map Dumped settings (later acceptable by Import ())
*/
global map Export () {
return $[];
}
/**
* Get Repository ID by index
*/
global integer GetSrcIdByIndex(integer idx) {
integer SrcId = sourceStatesOut[idx, "SrcId"]:-1;
return SrcId;
}
/**
* Set current used repository URL by index
*/
global void SetUrlByIndex(integer idx) {
integer SrcId = sourceStatesOut[idx, "SrcId"]:-1;
currentUrl = Pkg::SourceGeneralData(SrcId)["url"]:"";
return;
}
/**
* Get Repository ID when only URL is known
*/
global define integer getSourceId( string url ) {
numSources = size( sourceStatesOut );
integer i = 0;
integer id = -1;
while ( i < numSources )
{
map generalData = Pkg::SourceGeneralData(sourceStatesOut[i, "SrcId"]:-1 );
if ( generalData[ "url" ]:"" == url )
{
id = sourceStatesOut[i, "SrcId"]:-1;
break;
}
i = i + 1;
}
return id;
}
/**
* Gather Repository Metadata
*/
global define map SourceData(integer source) ``{
map g = Pkg::SourceGeneralData( source );
y2milestone("generalData: %1", g);
map p = Pkg::SourceProductData( source );
if (p == nil)
{
p = $[];
}
y2milestone("productData: %1", p);
return ((map)union(g,p));
}
/**
* Create a repository from an URL
*/
global symbol createSource( string url ) {
if ( url != "" )
{
if (!Mode::commandline())
{
UI::OpenDialog(
`VBox(
`VSpacing( 0.2 ),
`Label( _("Adding repository...") ),
`VSpacing( 0.2 )
)
);
}
newSources = Pkg::SourceScan( url, "" );
if (!Mode::commandline())
UI::CloseDialog();
if ( size( newSources ) == 0 )
{
string _msg1 = sformat( _("Unable to create repository
from URL '%1'."), URL::HidePassword(url) );
string _msg2 = _("Details:") + "\n" + Pkg::LastError();
// end of popup message, question
_msg2 = _msg2 + "\n" + _("Try again?");
boolean tryagain = Popup::YesNo( _msg1 + "\n" + _msg2 );
if ( tryagain ) return `again;
else return `cancel;
}
else
{
list<integer> ul_sources = filter (integer s, newSources, {
map src_data = Pkg::SourceGeneralData (s);
string src_type = src_data["type"]:"";
return src_type == "YaST";
});
if (size (ul_sources) == 0)
{
if (! Popup::AnyQuestion (
Popup::NoHeadline (),
// continue-back popup
_("There is no product information available at the given location.
If you expected to address a product, return back and enter
the correct location.
To make rpm packages located at the specified location available
in the packages selection, continue."),
Label::ContinueButton (),
Label::BackButton (),
`focus_yes))
{
return `again;
}
}
foreach( integer id, newSources, ``{
map<string, any> sourceState = $[ "SrcId": id, "enabled": true ];
sourceStatesOut = add( sourceStatesOut, sourceState );
} );
return `ok;
}
}
return `cancel;
}
/**
* Delete repository by Repository ID
*/
global void deleteSourceBySrcId( integer SrcId ) {
y2debug("removing repository: %1 %2", SrcId, sourceStatesOut );
numSources = size( sourceStatesOut );
integer i = 0;
while ( i < numSources )
{
if ( sourceStatesOut[i, "SrcId"]:-1 == SrcId )
{
sourceStatesOut = remove( sourceStatesOut, i );
break;
}
i = i + 1;
}
return;
}
/**
* Delete Repository by the repository index
*/
global void deleteSourceByIndex (integer idx ) {
sourceStatesOut = remove( sourceStatesOut, idx );
return;
}
/**
* Delete Repository by repository URL
*/
global void deleteSourceByUrl (string url ) {
deleteSourceBySrcId(getSourceId(url));
return;
}
/**
* Create Summary Item
*/
define string createItem( integer index, map source ) {
integer id = source[ "SrcId" ]:0;
map generalData = Pkg::SourceGeneralData( id );
map productData = Pkg::SourceProductData( id );
string sitem = "";
string status = source[ "enabled" ]:true
// status info, to be used inside summary
? _("Enabled")
// status info, to be used inside summary
: _("Disabled");
string color = source[ "enabled" ]:true ? "#006600" : "#FF0000";
sitem = sitem + HTML::Colorize("["+status+"] ",
color);
// translators: name of a repository if no other idenfication found
sitem = sitem + productData[ "label" ]:generalData["type"]:_("unknown");
sitem = sitem + " ( " + generalData[ "url" ]:"" + ")";
return sitem;
}
/**
* Create Repository Item for Overview
*/
define term createOverviewItem( integer index, map source ) {
integer id = source[ "SrcId" ]:0;
map generalData = Pkg::SourceGeneralData( id );
map productData = Pkg::SourceProductData( id );
term item = `item(
`id(index ),
source[ "enabled" ]:true
// corresponds to the "Enable/Disable" button
? _("On")
// corresponds to the "Enable/Disable" button
: _("Off"),
productData[ "label" ]:generalData["type"]:_("Unknown"),
generalData[ "url" ]:""
);
return item;
}
/**
* Handle Multiple repositories URLs (order/instorder)
*/
boolean HandleMultipleSources( string url ) {
boolean metadir_used = false;
list<string> theSourceDirectories = [];
map<integer,integer> theSourceOrder = $[];
list theSources=[];
string tmpdir = (string) SCR::Read(.target.tmpdir );
string metadir = tmpdir + "/yast-install";
Pkg::SourceStartManager( false );
integer initial_source = Pkg::SourceScan(url, "")[0]:nil;
if (initial_source == nil)
{
y2error ("No repository in '%1'", url);
return false;
}
return false;
}
/**
* Create a textual summary and a list of unconfigured cards
* @return summary of the current configuration
*/
global list Summary() {
string summary = "";
// summary header
summary = Summary::AddHeader(summary, _("Configured Repositories"));
summary = Summary::OpenList(summary);
numSources = size( sourceStatesOut );
integer i = 0;
while ( i < numSources ) {
summary = Summary::AddListItem(summary,createItem(i, sourceStatesOut[ i ]:$[]));
i = i + 1;
}
summary = Summary::CloseList(summary);
return [summary, [] ];
}
/**
* Create an overview table with all configured cards
* @return table items
*/
global list Overview() {
numSources = size( sourceStatesOut );
integer i = 0;
list source_overview = [];
while ( i < numSources ) {
source_overview = add(source_overview,
createOverviewItem(i, sourceStatesOut[ i ]:$[] ));
i = i + 1;
}
return source_overview;
}
/**
* Obsoleted (empty implementation), ZMD (ZLM7.3) on sle11 supports only HTTP and FTP repositories,
* sync would fail for other types. See bnc#480845 for more details.
*
* @param added_src_ids dummy parameter
* @param deleted_src_ids dummy parameter
* @return success - always true
*/
/* TODO FIXME: remove when it's not used in other modules */
global boolean SyncAddedAndDeleted (list<integer> added_src_ids,
list<integer> deleted_src_ids) {
y2warning(-1, "SourceManager::SyncAddedAndDeleted() is obsoleted (does nothing). ZMD sychronization has been removed (see bnc#480845 for details). Backtrace follows:");
return true;
}
/**
* Parse a URL query (already unescaped) to a map.
* If no equal sign, the value will be nil.
* @param query foo=bar&baz=qux
* @return $["foo": "bar", "baz": "qux"]
*/
map<string, string> ParseUrlQuery (string query) {
list<string> q_items = splitstring (query, "&");
map<string, string> q_map = listmap (string q_item, q_items, {
integer eqpos = search (q_item, "=");
if (eqpos == nil)
{
return $[ q_item: nil ];
}
else
{
string key = substring (q_item, 0, eqpos);
string val = substring (q_item, eqpos + 1);
return $[ key: val ];
}
});
return q_map;
}
/**
* @param attr SourceGeneralData item
* @return For existing repositories, get a mapping from an attribute to the id
*/
map<string,integer> get_attr_to_id (string attr) {
list<integer> src_ids = Pkg::SourceGetCurrent (false/*enabled only?*/);
map<string,integer> a2i = listmap (integer src_id, src_ids, {
map gendata = Pkg::SourceGeneralData (src_id);
string alias = gendata[attr]:"";
return $[ alias: src_id ];
});
return a2i;
}
/**
* @return For existing repositories, get a mapping from the alias to the id
*/
map<string,integer> get_alias_to_id () {
return get_attr_to_id ("alias");
}
/**
* @return For existing repositories, get a mapping from the URL to the id
*/
map<string,integer> get_url_to_id () {
return get_attr_to_id ("url");
}
/**
* Extract an alias parameter from the URL and check whether we have
* such a repository already.
* @param url a repository with an alias parameter (actually optional)
* @param alias_to_id a premade mapping, @see get_alias_to_id
* @return the repository id or -1
*/
integer SourceByAliasOrUrl (string url,
map<string,integer> alias_to_id,
map<string,integer> url_to_id) {
// parse the URL
map parsed_url = URL::Parse (url);
y2milestone ("parsed: %1", parsed_url);
// (reassemble and warn if it differs)
string reassembled = URL::Build (parsed_url);
if (url != reassembled)
{
y2warning ("reassembled differs: %1", reassembled);
}
// get the alias
map<string, string> q_map = ParseUrlQuery (parsed_url["query"]:"");
y2milestone ("query: %1", q_map);
string alias = q_map["alias"]:"";
// (empty: box safeguard)
if (alias != "" && haskey (alias_to_id, alias))
{
return alias_to_id[alias]:-1;
}
// #188572: if no match by alias, try url
return url_to_id[url]:-1;
}
/**
* Used by registration. ZMD sync has been disabled - ZLM7.3 on sle11 supports
* only HTTP and FTP repositories, sync would fail for other types.
* See bnc#480845 for more details.
*
* @param urls URLs to add
* @return a list of added URLs
*/
global list<string> AddUpdateSources (list<string> urls) {
list<string> ret = [];
// prepare for lookup of known aliases
map<string,integer> aliases = get_alias_to_id ();
y2milestone ("alias mapping: %1", aliases);
map<string,integer> by_url = get_url_to_id ();
y2milestone ("url mapping: %1", by_url);
// add the repositories
// but do not make duplicates (#168740)
// we detect them based on alias that suse_register gives us (#158850#c17)
/// (but only for SLE... :-/ )
/// Need to test what happens when we get two different update
/// servers for SL
/// Anyway that means only that #168740 remains unfixed for SL
foreach (string url, urls, {
y2milestone ("Should add an update repository: %1", url);
// inst_addon_update_sources also calls Pkg::SourceCreate
// but it already skips duplicates
// check if alias already there
// if yes, delete the old one
integer todel = SourceByAliasOrUrl (url, aliases, by_url);
if (todel != -1)
{
y2milestone ("deleting the old copy, repository %1", todel);
Pkg::SourceDelete (todel);
}
// then add the new one
y2milestone ("Adding update repository...");
integer toadd = Pkg::SourceCreate (url, "/");
// adding failed, try http fallback for ftp repo (#227059)
if (toadd == nil || toadd < 0)
{
map parsed_url = URL::Parse(url);
string scheme = parsed_url["scheme"]:"";
if (tolower(scheme) == "ftp")
{
y2milestone("Cannot add FTP update repository, trying HTTP...");
parsed_url["scheme"] = "http";
string fallback_url = URL::Build(parsed_url);
toadd = Pkg::SourceCreate (fallback_url, "/");
url = fallback_url;
}
}
if (toadd != -1 && toadd != nil)
{
ret = add (ret, url); // #180820#c26
// is there any patch available?
list patches = Pkg::ResolvableProperties("", `patch, "");
if (size(patches) > 0)
{
// loaded target is required to get list of applicable patches (#270919)
y2milestone("Repository %1 provides %2 patches, loading target...", url, size(patches));
// suppose that we are running in an installed system and use "/" directory
Pkg::TargetInitialize("/");
Pkg::TargetLoad();
}
}
});
return ret;
}
/**
*
*/
global map<string,any> AskForCD (string message) {
list<map<string,any> > cdroms = (list<map<string,any> >)
SCR::Read (.probe.cdrom);
boolean multiple_drives = size (cdroms) > 1;
term drives_sel = `Empty ();
list<term> devices = maplist (map<string,any> d, cdroms, {
return `item(`id(d["dev_name"]:""), d["model"]:"" + " (" + (d["dev_name"]:"") + ")" );
});
if (multiple_drives)
{
drives_sel = `SelectionBox (`id (`drives), _("&Drive to eject"),
devices);
}
term contents = `HBox (`HSpacing (1), `VBox (
`VSpacing (0.5),
`Label (message),
`VSpacing (0.5),
drives_sel,
`VSpacing (0.5),
`HBox (
`HStretch (),
`HWeight (1, `PushButton (`id (`cont), Label::ContinueButton ())),
`HWeight (1, `PushButton (`id (`cancel), Label::CancelButton ())),
`HWeight (1, `PushButton (`id (`eject), _("&Eject"))),
`HStretch ()
),
`VSpacing (0.5)
), `HSpacing (1));
UI::OpenDialog (contents);
if (multiple_drives)
UI::ChangeWidget (`id (`drives), `CurrentItem, cdroms[0,"dev_name"]:"");
UI::SetFocus (`id (`cont));
symbol ret = nil;
while (true)
{
ret = (symbol)UI::UserInput ();
if (ret == `cont || ret == `cancel)
{
break;
}
if (ret == `eject)
{
if (multiple_drives)
{
string device = (string)UI::QueryWidget (`id (`drives), `Value);
SCR::Execute (.target.bash, sformat ("/bin/eject %1", device));
}
else
{
SCR::Execute (.target.bash, sformat ("/bin/eject %1",
cdroms[0,"dev_name"]:""));
}
}
ret = nil;
}
map<string,any> result = $[ "continue" : (ret == `cont) ];
if (multiple_drives)
{
result = add(result, "device", (string)UI::QueryWidget (`id (`drives), `Value));
}
UI::CloseDialog ();
return result;
}
/**
* Function returns the partiton name which is used as a repository for the installation
* (IF any partition is used as a repository for installation, of course).
* Otherwise it returns an empty string "". See bugzilla #208222 for more information.
*
* @return string partition name
*/
global string InstallationSourceOnPartition () {
string install_mode = Linuxrc::InstallInf ("InstMode");
// Hard Disk is used for the installation
if (install_mode == "hd") {
string install_partition = Linuxrc::InstallInf ("Partition");
// No partiton is defined - error
if (install_partition == "" || install_partition == nil) {
y2error ("Despite the fact that the install-mode is '%1', install-partition is '%2'",
install_mode, install_partition
);
return "";
} else {
return install_partition;
}
} else {
return "";
}
}
/**
* Finds the biggest temporary directory and uses it as
* packager download area.
*/
global void InstInitSourceMoveDownloadArea () {
map spaces = Pkg::TargetGetDU ();
list root_info = spaces["/tmp"]:spaces["/tmp/"]:spaces["/"]:[];
integer total = root_info[0]:0;
integer current = root_info[1]:0;
integer future = root_info[2]:0;
if (future < current)
future = current;
integer tmp_space = total - future;
// no temp space left or read-only
if (tmp_space < 0 || root_info[3]:1 == 1)
tmp_space = 0;
list var_info = spaces["/var/tmp"]:spaces["/var/tmp/"]:spaces["/var"]:spaces["/var/"]:spaces["/"]:[];
total = var_info[0]:0;
current = var_info[1]:0;
future = var_info[2]:0;
if (future < current)
future = current;
integer var_tmp_space = total - future;
// no temp space left or read-only
if (var_tmp_space < 0 || var_info[3]:1 == 1)
var_tmp_space = 0;
//-------
// /tmp or /var/tmp ?
string download_dir = tmp_space > var_tmp_space
? "/tmp"
: "/var/tmp";
download_dir = Installation::destdir + download_dir;
integer space = tmp_space > var_tmp_space
? tmp_space
: var_tmp_space;
if (true) // TODO check the size of the largest package on CD1
{
integer successful = (integer) SCR::Execute (.target.bash, sformat ("test -d '%1' || mkdir -p '%1'", String::Quote (download_dir)));
if (successful == 0) {
Pkg::SourceMoveDownloadArea (download_dir);
} else {
y2error ("Unable to create %1 directory", download_dir);
}
}
}
/* EOF */
}
ACC SHELL 2018