home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / share / YaST2 / modules / SourceManager.ycp < prev    next >
Text File  |  2006-11-29  |  40KB  |  1,465 lines

  1. /**
  2.  * File:    modules/SourceManager.ycp
  3.  * Package:    Package Source Management
  4.  * Summary:    SourceManager settings, input and output functions
  5.  * Authors:    Anas Nashif <nashif@suse.de>
  6.  *        Lukas Ocilka <locilka@suse.cz>
  7.  *        Martin Vidner <mvidner@suse.cz>
  8.  * Status:      Work in Progress
  9.  *
  10.  * $Id: SourceManager.ycp 34515 2006-11-21 09:20:08Z mvidner $
  11.  *
  12.  * Representation of the configuration of source-manager.
  13.  * Input and output routines.
  14.  */
  15.  
  16. {
  17.  
  18. textdomain "packager";
  19. module "SourceManager";
  20.  
  21. import "Progress";
  22. import "Report";
  23. import "Popup";
  24. import "Label";
  25. import "Summary";
  26. import "Message";
  27. import "HTML";
  28. import "Arch";
  29. import "Mode";
  30. import "Stage";
  31. import "URL";
  32. import "InstURL";
  33. import "String";
  34. import "SuSEFirewall";
  35. import "SourceManagerSLP";
  36. import "Linuxrc";
  37.  
  38.  
  39. global list<integer> newSources = [];
  40.  
  41. global integer numSources = 0;
  42.     
  43. global list<integer> sourceStates = [];
  44.  
  45. global list<map<string,any> > sourceStatesIn = [];
  46.  
  47. global list<map<string,any> > sourceStatesOut = [];
  48.  
  49.  
  50. list<map> slp_sources = [];
  51.  
  52. string pm_init_blocker = "";
  53.  
  54. global map url_tokens = $[];
  55.  
  56. global string currentUrl = "";
  57.  
  58. /**
  59.  * Prototypes
  60.  */
  61. global boolean Modified();
  62. global symbol createSource( string url );
  63.  
  64. /**
  65.  * Data was modified?
  66.  */
  67. global boolean modified = false;
  68.  
  69. /**
  70.  */
  71. global boolean proposal_valid = false;
  72.  
  73. /**
  74.  * Abort function
  75.  * return boolean return true if abort
  76.  */
  77. global boolean AbortFunction() {
  78.     return false;
  79.     }
  80.  
  81. /**
  82.  * Abort function
  83.  * @return boolean return true if abort
  84.  */
  85. global define boolean Abort() ``{
  86.     if(AbortFunction != nil)
  87.     {
  88.     return AbortFunction () == true;
  89.     }
  90.     return false;
  91. }
  92.  
  93. /**
  94.  * Data was modified?
  95.  * @return true if modified
  96.  */
  97. global boolean Modified() {
  98.     y2debug("modified=%1",modified);
  99.     //return modified;
  100.     return (sourceStatesIn != sourceStatesOut);
  101. }
  102.  
  103. global boolean ReadSources()
  104. {
  105.     boolean success = Pkg::SourceStartManager( false );
  106.     if (!success)
  107.         return success;
  108.     sourceStates = Pkg::SourceStartCache ( false );
  109.     sourceStatesIn = Pkg::SourceEditGet();
  110.     sourceStatesOut = sourceStatesIn;
  111.     return true;
  112. }
  113.  
  114. /**
  115.  * Function scans for SLP installation servers on the network
  116.  * @returns symbol one of `back, `next
  117.  */
  118. global string AddSourceTypeSLP () {
  119.     string url = SourceManagerSLP::SelectOneSLPService();
  120.     y2milestone("Selected URL: %1", url);
  121.  
  122.     return url;
  123. }
  124.  
  125. /**
  126.  * Read all source-manager settings
  127.  * @return true on success
  128.  */
  129. global boolean Read() {
  130.  
  131.     /* SourceManager read dialog caption */
  132.     string caption = _("Initializing Available Catalogs");
  133.  
  134.     integer steps = 2;
  135.  
  136.  
  137.     // We do not set help text here, because it was set outside
  138.     Progress::New( caption, " ", steps, [
  139.         /* Progress stage 1/3 */
  140.         _("Read configured catalogs"),
  141.         /* Progress stage 2/3 */
  142.         _("Detect available catalogs via SLP")
  143.     ], [
  144.         /* Progress step 1/3 */
  145.         _("Reading configured catalogs..."),
  146.         /* Progress step 2/3 */
  147.         _("Detecting available catalogs..."),
  148.         /* Progress finished */
  149.         _("Finished")
  150.     ],
  151.     ""
  152.     );
  153.  
  154.     // read database
  155.     if(Abort()) return false;
  156.     Progress::NextStage();
  157.  
  158.  
  159.     /* Error message */
  160.     if(!ReadSources()) Report::Error(_("Cannot read catalogs."));
  161.  
  162.     // read another database
  163.     if(Abort()) return false;
  164.     Progress::NextStep();
  165.  
  166.  
  167.     /* Error message */
  168.     if(false) Report::Error(_("Cannot detect available catalogs."));
  169.  
  170.     if(Abort()) return false;
  171.     /* Progress finished */
  172.     Progress::NextStage();
  173.  
  174.     // slp_sources = ReadSLPSources();
  175.     
  176.     y2debug ("slp catalogs: %1", slp_sources);
  177.  
  178.     if(Abort()) return false;
  179.     modified = false;
  180.     return true;
  181. }
  182.  
  183. /**
  184.  * Commit changed sources
  185.  */ 
  186. global boolean CommitSources() {
  187.     y2debug("In: %1  Out: %2",   sourceStatesIn, sourceStatesOut );
  188.     boolean success = false;
  189.     while (true)
  190.     {
  191.         success = Pkg::SourceEditSet( sourceStatesOut );
  192.         if ( !success ) {
  193.         // popup message header
  194.             string _msg1 = _("Unable to save changes to the catalog
  195.                     repository.
  196. ");
  197.         // popup message, after message header, header of details
  198.             string _msg2 = _("Details:") + "\n" + Pkg::LastError();
  199.         // end of popup message, question
  200.             _msg2 = _msg2 + "\n" + _("Try again?");
  201.  
  202.             boolean tryagain = Popup::YesNo( _msg1 + "\n" + _msg2 );
  203.             if (tryagain )
  204.                 continue;
  205.             else
  206.                 break;
  207.         }
  208.         else
  209.         {
  210.             break;
  211.         }
  212.     }
  213.     return success;
  214. }
  215.  
  216. /**
  217.  * Write all source-manager settings
  218.  * @return true on success
  219.  */
  220. global boolean Write() {
  221.     
  222.     /* SourceManager read dialog caption */
  223.     string caption = _("Saving Catalog Configuration");
  224.  
  225.     integer steps = 1;
  226.  
  227.     // We do not set help text here, because it was set outside
  228.     Progress::New(caption, " ", steps, [
  229.         /* Progress stage 1/1 */
  230.         _("Write catalog settings"),
  231.     ], [
  232.         /* Progress step 1/1 */
  233.         _("Writing the settings..."),
  234.  
  235.         /* Progress finished */
  236.         _("Finished")
  237.     ],
  238.     ""
  239.     );
  240.  
  241.     // write settings
  242.     if(Abort()) return false;
  243.    
  244.     Progress::NextStage();
  245.     /* Error message */
  246.  
  247.     boolean exit = CommitSources();
  248.  
  249.         
  250.     
  251.     if(Abort()) return false;
  252.     /* Progress finished */
  253.     Progress::NextStage();
  254.  
  255.     if(Abort()) return false;
  256.     
  257.     return exit;
  258. }
  259.  
  260. /**
  261.  * Get all source-manager settings from the first parameter
  262.  * (For use by autoinstallation.)
  263.  * @param settings The YCP structure to be imported.
  264.  * @return boolean True on success
  265.  */
  266. global boolean Import (map settings) {
  267.     return true;
  268. }
  269.  
  270. /**
  271.  * Dump the source-manager settings to a single map
  272.  * (For use by autoinstallation.)
  273.  * @return map Dumped settings (later acceptable by Import ())
  274.  */
  275. global map Export () {
  276.     return $[];
  277. }
  278.  
  279.  
  280.  
  281.  
  282. global boolean CreateLocalMetaData ( 
  283.         integer SrcId, 
  284.         string metaDir,
  285.         string slidedir,
  286.         string descrDir, 
  287.         string prodDir) {
  288.  
  289.     boolean  all_sources_ok=true;
  290.     string localdir = metaDir+"/"+descrDir;
  291.     WFM::Execute (.local.mkdir, localdir);
  292.     y2debug("created local dir : %1", localdir );
  293.     string datadir = Pkg::SourceProvideDir (SrcId, 1, descrDir+"/media.1");
  294.     if (datadir != nil)
  295.     {
  296.         WFM::Execute (.local.mkdir, localdir+"/media.1");
  297.         WFM::Execute (.local.bash, "/bin/cp " + datadir + "/* " + localdir+"/media.1");
  298.     }
  299.     else
  300.     {
  301.         y2error ("media doesn't provide %1", descrDir + "/media.1");
  302.         if (Popup::AnyQuestion (
  303.                     //    Popup::NoHeadline(),
  304.             // popup headline
  305.                     _("Reading Media Description Failed"),
  306.                     sformat (
  307.             // popup message, %1 is filename
  308.                         _("Cannot read media description for %1.
  309.                             Packages from this media cannot be installed."), descrDir),
  310.                     Label::IgnoreButton (),
  311.                     Label::AbortButton (),
  312.                     `cancel))
  313.         {
  314.             return false;
  315.         }
  316.         else
  317.         {
  318.             all_sources_ok = false;
  319.         }
  320.     }
  321.  
  322.     // content file
  323.     datadir = Pkg::SourceProvideFile (SrcId, 1, descrDir+"/content");
  324.     WFM::Execute (.local.bash, "/bin/cp " + datadir + " " + localdir + "/content");
  325.  
  326.     // DESCRDIR
  327.     WFM::Execute (.local.bash, "/bin/grep DESCRDIR " + localdir + "/content > /tmp/descrdir");
  328.     string descrline = (string) WFM::Read (.local.string, "/tmp/descrdir");
  329.     list<string> descrsplit = splitstring (descrline, " \t\n");
  330.     string _descrdir = descrsplit[1]:"";
  331.     if (size (_descrdir) > 0)
  332.     {
  333.         y2milestone ("descrdir %1", _descrdir);
  334.         WFM::Execute (.local.mkdir, localdir+"/"+_descrdir);
  335.         datadir = Pkg::SourceProvideDir (SrcId, 1, descrDir+"/"+_descrdir);
  336.         if (datadir != nil)
  337.         {
  338.             WFM::Execute (.local.bash, "/bin/cp " + datadir + "/* " +localdir+"/"+_descrdir);
  339.         } else {
  340.             y2error("Error providing %1", _descrdir );
  341.         }
  342.     }
  343.  
  344.     // copy GPG keys if there are any
  345.  
  346.     string directory_file = Pkg::SourceProvideFile (
  347.     SrcId, 1, descrDir + "/directory.yast");
  348.  
  349.     string directory_str = (string)
  350.     WFM::Read (.local.string, directory_file);
  351.     list<string> directory = splitstring (directory_str, "\n");
  352.     directory = filter (string d, directory, {
  353.     return substring (d, 0, 10) == "gpg-pubkey";
  354.     });
  355.     y2milestone ("GPG keys to be copied: %1", directory);
  356.     foreach (string k, directory, {
  357.     string file = Pkg::SourceProvideFile (
  358.         SrcId, 1, descrDir + "/" + k);
  359.     WFM::Execute (.local.bash,
  360.         "cp /" + file + " " + localdir);
  361.     });
  362.  
  363.     // slide show
  364.  
  365.     if (size (_descrdir) > 5)
  366.     {
  367.         string tmp = substring (_descrdir, 0, size (_descrdir) - 5) + "slide";
  368.         y2milestone("slide dir: %1", tmp );
  369.         WFM::Execute (.local.mkdir, localdir+"/"+tmp);
  370.         // FIXME: SourceProvideDir can't handle dirs with subdirs
  371.         datadir = Pkg::SourceProvideDir (SrcId, 1, descrDir+"/"+tmp);
  372.         // datadir = "/var/adm/YaST/InstSrcManager/IS_CACHE_0x00000001/MEDIA" + orderdir+"/"+tmp;
  373.         y2milestone ("tmp=%1, datadir=%2", tmp, datadir);
  374.         if (datadir != nil)
  375.         {
  376.             y2milestone ("copy slide show %1 %2", datadir, slidedir);
  377.             WFM::Execute (.local.bash, "/bin/cp -r " + datadir + "/* " + slidedir);
  378.  
  379.             // if directory is empty, remove it.
  380.             WFM::Execute (.local.bash, "/bin/rmdir " + slidedir);
  381.         }
  382.     }
  383.  
  384.     return all_sources_ok;
  385.  
  386. }
  387.  
  388.  
  389. /**
  390.  * Get Source ID by index 
  391.  */
  392. global integer GetSrcIdByIndex(integer idx)   {
  393.     
  394.     integer SrcId = sourceStatesOut[idx, "SrcId"]:-1;
  395.   
  396.     return SrcId;
  397. }
  398.  
  399. /**
  400.  * Set current used source URL by index 
  401.  */
  402. global void SetUrlByIndex(integer idx)   {
  403.     
  404.     integer SrcId = sourceStatesOut[idx, "SrcId"]:-1;
  405.     currentUrl = Pkg::SourceGeneralData(SrcId)["url"]:"";
  406.     return;
  407. }
  408.  
  409.  
  410. /**
  411.  * Get Source ID when only URL is known 
  412.  */
  413. global define integer getSourceId( string url ) {
  414.     
  415.     numSources = size( sourceStatesOut );
  416.     integer i = 0;
  417.     integer id = -1;
  418.     while ( i < numSources )
  419.     { 
  420.         map generalData = Pkg::SourceGeneralData(sourceStatesOut[i, "SrcId"]:-1 );
  421.     if ( generalData[ "url" ]:"" == url )
  422.     {
  423.         id = sourceStatesOut[i, "SrcId"]:-1;
  424.         break;
  425.     }
  426.  
  427.     i = i + 1;
  428.     }
  429.     
  430.     return id;
  431. }
  432.  
  433.  
  434.  
  435.  
  436.  
  437.  
  438. /**
  439.  * Gather Source Metadata
  440.  */
  441. global define map SourceData(integer source) ``{
  442.     map g =  Pkg::SourceGeneralData( source );
  443.     y2milestone("generalData: %1", g);
  444.     map p =  Pkg::SourceProductData( source );
  445.     if (p == nil)
  446.     {
  447.     p = $[];
  448.     }
  449.  
  450.     y2milestone("productData: %1", p);
  451.     return ((map)union(g,p));
  452. }
  453.  
  454. /**
  455.  * Create a Source from an URL
  456.  */
  457. global symbol createSource( string url ) {
  458.     
  459.     if ( url != "" )
  460.     {
  461.     if (!Mode::commandline())
  462.     {
  463.         // Popup::Message( sformat( "URL: %1", url ) );
  464.         UI::OpenDialog(
  465.                `VBox(
  466.                  `VSpacing( 0.2 ),
  467.                  `Label( _("Adding catalog...") ),
  468.                  `VSpacing( 0.2 )
  469.                  )
  470.                );
  471.     }
  472.     newSources = Pkg::SourceScan( url, "" );
  473.  
  474.     if (!Mode::commandline())    
  475.         UI::CloseDialog();
  476.  
  477.     
  478.     if ( size( newSources ) == 0  )
  479.     {
  480.         string _msg1 = sformat( _("Unable to create catalog
  481. from URL '%1'."), InstURL::HidePassword(url) );
  482.  
  483.         string _msg2 = _("Details:") + "\n" + Pkg::LastError();
  484.         // end of popup message, question
  485.         _msg2 = _msg2 + "\n" + _("Try again?");
  486.  
  487.         boolean tryagain = Popup::YesNo( _msg1 + "\n" + _msg2 );
  488.         if ( tryagain ) return `again;
  489.         else return `cancel;
  490.     }
  491.     else
  492.     {
  493.         list<integer> ul_sources = filter (integer s, newSources, {
  494.         map src_data = Pkg::SourceGeneralData (s);
  495.         string src_type = src_data["type"]:"";
  496.         return src_type == "YaST";
  497.         });
  498.         if (size (ul_sources) == 0)
  499.         {
  500.         if (! Popup::AnyQuestion (
  501.                       Popup::NoHeadline (),
  502. // continue-back popup
  503.                       _("There is no product information available at the given location.
  504. If you expected to address a product, return back and enter
  505. the correct location.
  506. To make rpm packages located at the specified location available
  507. in the packages selection, continue."),
  508.                       Label::ContinueButton (),
  509.                       Label::BackButton (),
  510.                       `focus_yes))
  511.         {
  512.             return `again;
  513.         }
  514.         }
  515.         foreach( integer id, newSources, ``{
  516.         map<string, any> sourceState = $[ "SrcId": id, "enabled": true ];
  517.         sourceStatesOut = add( sourceStatesOut, sourceState ); 
  518.         } );
  519.         return `ok;
  520.     }
  521.     }
  522.     return `cancel;
  523. }
  524.  
  525. /**
  526.  * Delete Source by Source ID
  527.  */
  528. global void deleteSourceBySrcId( integer SrcId )   {
  529.  
  530.     y2debug("removing source: %1 %2", SrcId,   sourceStatesOut );
  531.     numSources = size( sourceStatesOut );
  532.     integer i = 0;
  533.     
  534.     while ( i < numSources )
  535.     {
  536.  
  537.     if ( sourceStatesOut[i, "SrcId"]:-1 == SrcId )
  538.     {        
  539.         sourceStatesOut = remove( sourceStatesOut, i );
  540.         break;
  541.     }        
  542.  
  543.     i = i + 1;
  544.     }
  545.     return;
  546.     
  547. }
  548.  
  549.  
  550. /**
  551.  * Delete Source by Source Index
  552.  */
  553. global void deleteSourceByIndex (integer idx ) {
  554.     
  555.     sourceStatesOut = remove( sourceStatesOut, idx );
  556.     return;
  557. }
  558.     
  559. /**
  560.  * Delete Source by Source URL
  561.  */
  562. global void deleteSourceByUrl (string url ) {
  563.     deleteSourceBySrcId(getSourceId(url));    
  564.     return;
  565. }
  566.  
  567. /**
  568.  * Create Summary Item
  569.  */
  570. define string createItem( integer index, map source ) {
  571.     integer id = source[ "SrcId" ]:0;
  572.     map generalData = Pkg::SourceGeneralData( id );
  573.     map productData = Pkg::SourceProductData( id );
  574.     string sitem = "";
  575.     string status = source[ "enabled" ]:true
  576.     // status info, to be used inside summary
  577.     ? _("Enabled")
  578.     // status info, to be used inside summary
  579.     : _("Disabled");
  580.     string color = source[ "enabled" ]:true ? "#006600" : "#FF0000";
  581.     sitem = sitem + HTML::Colorize("["+status+"] ",
  582.             color);
  583.     // translators: name of a source if no other idenfication found
  584.     sitem = sitem + productData[ "label" ]:generalData["type"]:_("unknown");
  585.     sitem = sitem + " ( " + generalData[ "url" ]:"" + ")";
  586.     return sitem;
  587. }
  588.  
  589.  
  590. /**
  591.  * Create Source Item for Overview
  592.  */
  593. define term  createOverviewItem( integer index, map source ) {
  594.     integer id = source[ "SrcId" ]:0;
  595.     map generalData = Pkg::SourceGeneralData( id );
  596.     map productData = Pkg::SourceProductData( id );
  597.       
  598.  
  599.     term item = `item(
  600.               `id(index ),
  601.               source[ "enabled" ]:true
  602.               // corresponds to the "Enable/Disable" button
  603.             ? _("On")
  604.               // corresponds to the "Enable/Disable" button
  605.             : _("Off"),
  606.               productData[ "label" ]:generalData["type"]:_("Unknown"),
  607.               generalData[ "url" ]:""
  608.               );    
  609.    
  610.     return item;
  611. }
  612.  
  613.  
  614. /**
  615.  * Handle Multiple source URLs (order/instorder)
  616.  */ 
  617. boolean HandleMultipleSources( string url )    {
  618.  
  619.     boolean metadir_used = false;
  620.     list<string> theSourceDirectories = [];
  621.     map<integer,integer> theSourceOrder = $[];
  622.  
  623.     list theSources=[];
  624.     string tmpdir = (string) SCR::Read(.target.tmpdir );
  625.     string metadir = tmpdir + "/yast-install";
  626.  
  627.  
  628.     Pkg::SourceStartManager( false );
  629.     integer initial_source = Pkg::SourceScan(url, "")[0]:nil;
  630.     if (initial_source == nil)
  631.     {
  632.         y2error ("No source on '%1'", url);
  633.         return false;
  634.     }
  635.  
  636.     return false;
  637. }
  638.  
  639.  
  640. /**
  641.  * Create a textual summary and a list of unconfigured cards
  642.  * @return summary of the current configuration
  643.  */
  644. global list Summary() {
  645.  
  646.     string summary = "";
  647.     // summary header
  648.     summary = Summary::AddHeader(summary, _("Configured Catalogs"));
  649.     summary = Summary::OpenList(summary);
  650.     numSources = size( sourceStatesOut );
  651.     integer i = 0;
  652.     while ( i < numSources ) {
  653.        summary = Summary::AddListItem(summary,createItem(i, sourceStatesOut[ i ]:$[]));
  654.         i = i + 1;
  655.     }
  656.     summary = Summary::CloseList(summary);
  657.  
  658.  
  659.   
  660.     list unconf = maplist(map s, slp_sources, ``{
  661.     string id = substring(s["srvurl"]:"", 21);
  662.     y2debug("source url : %1", id );
  663.     // part of item in summary
  664.     return(`item(`id(id), s["attr", "label"]:_("Unknown")
  665.         // part of item in summary meaning "switched on"
  666.         + _(" On ") + s["pcHost"]:""));
  667.     });   
  668.  
  669.     return [summary, unconf ];
  670. }
  671.  
  672. /**
  673.  * Create an overview table with all configured cards
  674.  * @return table items
  675.  */
  676. global list Overview() {
  677.     numSources = size( sourceStatesOut );
  678.     integer i = 0;
  679.     list source_overview = [];
  680.     while ( i < numSources ) {
  681.     source_overview = add(source_overview,
  682.                 createOverviewItem(i, sourceStatesOut[ i ]:$[] ));
  683.         i = i + 1;
  684.     }
  685.     return source_overview;
  686. }
  687.  
  688.     // ------------------------------------------------------------------------------------------------------
  689.     // adding YaST installation source into the ZMD
  690.  
  691.     global string SyncLabel () {
  692.     // yast is running a zenworks command (rug) to
  693.     // keep the repository references in sync
  694.     return _("Synchronizing with ZENworks");
  695.     }
  696.  
  697.     /**
  698.      * Runs a bash command with timeout.
  699.      * @struct Returns map $[
  700.      *     "exit" : int_return_code,
  701.      *     "stdout"  : [ "script", "stdout", "lines" ],
  702.      *     "stderr"  : [ "script", "stderr", "lines" ],
  703.      * ]
  704.      *
  705.      * @param run_command what to run
  706.      * @param log_command what to log (passwords masked)
  707.      * @param script_time_out in sec.
  708.      * @return map with out, err and ret_code
  709.      */
  710.     global map RunCommandWithTimeout (string run_command, string log_command, integer script_time_out) {
  711.     y2milestone("Running command \"%1\" in background...", log_command);
  712.  
  713.     boolean started = (boolean) SCR::Execute(.background.run_output_err, run_command);
  714.     if (!started) {
  715.         y2error("Cannot run '%1'", run_command);
  716.         return nil;
  717.     }
  718.     
  719.     list<string> script_out = [];
  720.     list<string> script_err = [];
  721.     integer time_spent = 0;
  722.     integer return_code = nil;
  723.     boolean timed_out = false;
  724.     integer sleep_step = 200; // ms
  725.     script_time_out = script_time_out * 1000;
  726.     
  727.     // while continuing is needed and while it is possible
  728.     while (! timed_out) {
  729.         boolean running = (boolean) SCR::Read(.background.isrunning);
  730.         // debugging #165821
  731.         if (time_spent % 100000 == 0) {
  732.         y2milestone ("running: %1", running);
  733.         string flag = "/tmp/SourceManagerTimeout";
  734.         if (SCR::Read (.target.size, flag) != -1) {
  735.             y2milestone ("Emergency exit");
  736.             SCR::Execute (.target.remove, flag);
  737.             break;
  738.         }
  739.         }
  740.  
  741.         if (! running) {
  742.         break;
  743.         }
  744.  
  745.         // at last, enable aborting the sync
  746.         if (Mode::commandline ()) {
  747.         sleep (sleep_step);
  748.         }
  749.         else {
  750.         any ui = UI::TimeoutUserInput (sleep_step);
  751.         if (ui == `abort) {
  752.             y2milestone ("aborted");
  753.             timed_out = true;
  754.             break;
  755.         }
  756.         }
  757.  
  758.         // time-out
  759.         if (time_spent >= script_time_out) {
  760.         y2error("Command timed out after %1 msec", time_spent);
  761.         timed_out = true;
  762.         }
  763.         
  764.         time_spent = time_spent + sleep_step;
  765.     }
  766.     y2milestone("Time spent: %1 msec", time_spent);
  767.  
  768.     // fetching the return code if not timed-out
  769.     if (! timed_out) {
  770.         y2milestone ("getting output");
  771.         script_out  = (list<string>) SCR::Read(.background.newout);
  772.         y2milestone ("getting errors");
  773.         script_err  = (list<string>) SCR::Read(.background.newerr);
  774.         y2milestone ("getting status");
  775.         return_code = (integer) SCR::Read(.background.status);
  776.     }
  777.     y2milestone ("killing");
  778.     SCR::Execute(.background.kill, "");
  779.  
  780.     map command_ret = $[
  781.         "exit"   : return_code,
  782.         "stdout" : script_out,
  783.         "stderr" : script_err,
  784.     ];
  785.     if (timed_out)
  786.         command_ret["timed_out"]    = time_spent;
  787.     return command_ret;
  788.     }
  789.  
  790.     /**
  791.      * Run
  792.      * - with a timeout
  793.      * - on dumb terminal to disable colors etc
  794.      * - using 'exit $?' because of buggy behavior '.background vs. ZMD'
  795.      * @param command a command
  796.      * @param log_command a command to log
  797.      * @param seconds timeout
  798.      * @return map with out, err and ret_code
  799.      */
  800.     global map RunDumbTimeout (string command, string log_command, integer seconds) {
  801.     string dumb_format = "export TERM=dumb; %1; exit $?";
  802.     string dumb_command = sformat (dumb_format, command);
  803.     string dumb_log_command = sformat (dumb_format, log_command);
  804.     // explicit export in case TERM was not in the environment
  805.     map ret = RunCommandWithTimeout (dumb_command, dumb_log_command, seconds);
  806.     if (ret == nil) ret = $[];
  807.     return ret;
  808.     }
  809.  
  810.     /**
  811.      * Run with a long timeout
  812.      * @param command a command
  813.      * @param log_command a command to log
  814.      * @return map with out, err and ret_code
  815.      */
  816.     map RunLongLog (string command, string log_command) {
  817.     return RunDumbTimeout (command, log_command, 1800);
  818.     }
  819.  
  820.     /**
  821.      * Run with a long timeout
  822.      * @param command a command
  823.      * @return map with out, err and ret_code
  824.      */
  825.     map RunLong (string command) {
  826.     return RunLongLog (command, command);
  827.     }
  828.  
  829.     /**
  830.      * path to ZMD CLI
  831.      */
  832.     const string rug = "/usr/bin/rug";
  833.  
  834.     /**
  835.      * Detect whether ZMD is running
  836.      */
  837.     boolean CheckZMDStatus () {
  838.     map zmd_status = RunLong (rug + " ping >/dev/null");
  839.     y2milestone("ZMD status: %1, err: %2", zmd_status["exit"]:nil, zmd_status["stderr"]:[]);
  840.     // Argh zis suks so mutch! #170549
  841.     return zmd_status["exit"]:nil == 0 || zmd_status["stderr"]:[] == [];
  842.     }
  843.  
  844.     list<string> known_urls = nil;
  845.  
  846.     /**
  847.      * Force calling rug on next @ref IsUrlKnownToZMD
  848.      */
  849.     void ResetKnownServiceCache () {
  850.     known_urls = nil;
  851.     }
  852.  
  853.     /**
  854.      * Update the cache after a successful rug service-{add,delete} call
  855.      * @param adding add url or delete it
  856.      * @param url what
  857.      */
  858.     void UpdateKnownServiceCache (boolean adding, string url) {
  859.     if (adding) {
  860.         known_urls = add (known_urls, url);
  861.     } else {
  862.         known_urls = filter (string u, known_urls, ``( u != url ));
  863.     }
  864.     }
  865.  
  866.     /**
  867.      * Whether the URL is known to rug service-list.
  868.      * The known services are cached,
  869.      * the caller should use @ref ResetKnownServiceCache if appropriate
  870.      * (each time when coming from outside this module at least)
  871.      * @return boolean or nil if determining the status failed
  872.      */
  873.     boolean IsUrlKnownToZMD (string url) {
  874.     if (known_urls == nil)
  875.     {
  876.         //format: "3|Active|ZYPP|ServiceName|ftp://example.org/update/10.1"
  877.         // With empty lines and "Waking up ZMD...Done" as a distraction
  878.         map ret = RunLong (rug + " --no-abbrev --terse service-list | cut -d'|' -f5 --only-delimited");
  879.         if (ret["stdout"]:nil == nil) {
  880.         y2error("Listing of services failed, returned %1", ret);
  881.         return nil;
  882.         }
  883.         // really individual lines?
  884.         known_urls = (list <string>) ret["stdout"]:[];
  885.         list<string> log_known_urls = maplist (string u, known_urls, ``(
  886.                                InstURL::HidePassword(u)
  887.                                ));
  888.         y2milestone ("known %1", log_known_urls);
  889.     }
  890.     return contains (known_urls, url);
  891.     }
  892.  
  893.     /**
  894.      * Get a ZMD preference
  895.      * @param pref see "rug get-prefs" 
  896.      */
  897.     string RugGetPref (string pref) {
  898.     // "pref|value"
  899.     map ret = RunLong (sformat ("%1 --terse get-prefs %2 | cut -d'|' -f2 --only-delimited", rug, pref));
  900.     if (ret["stdout"]:nil == nil) {
  901.         y2error("GetPref failed, returned %1", ret);
  902.         return nil;
  903.     }
  904.     string line = ret["stdout", 0]:"";
  905.     return deletechars (line, "\n");
  906.     }
  907.  
  908.     /**
  909.      * Set a ZMD preference
  910.      */
  911.     void RugSetPref (string pref, string value) {
  912.     map ret = RunLong (sformat ("%1 set-prefs %2 '%3'", rug, pref, value));
  913.     y2milestone ("ret %1", ret);
  914.     }
  915.  
  916.     /**
  917.      * Adds a ZYPP service into ZMD
  918.      * @param src_id installation source id
  919.      * @return success
  920.      */
  921.     boolean AddOrDeleteZYPPServiceIntoZMD (integer src_id, boolean adding) {
  922.     map gendata = Pkg::SourceGeneralData (src_id);
  923.     string stype = gendata["type"]:""; // metadata type
  924.     if (stype == "YaST") stype = "zypp";    /* 'rug' only accepts yum or zypp  */
  925.     string url = gendata["url"]:"";
  926.     string log_url = InstURL::HidePassword (url);
  927.     // ZMD must have unique URIs, so we append the alias
  928.     string alias = gendata["alias"]:"";
  929.     y2milestone ("id: %1, type: %2, url: %3, alias: %4", src_id, stype, log_url, alias);
  930.     if (stype == "" || url == "" || alias == "") {
  931.         y2error ("Internal error: stype, url, or alias is empty ?!");
  932.         return false;
  933.     }
  934.  
  935.     // yum sources do not need+understand the alias, #164083
  936.     // The above reason is obsolete but we keep compatibility with
  937.     // the service-delete zmd helper
  938.     if (stype == "zypp")
  939.     {
  940.         // URL (un)escapes for us
  941.         // maybe a bug with "="?
  942.         map parsed_url = URL::Parse (url);
  943.         string query = parsed_url["query"]:"";
  944.         if (query != "") query = query + "&";
  945.         parsed_url["query"] = query + "alias=" + alias;
  946.         string test_url = URL::Build (parsed_url);
  947.         y2milestone ("test zmd url: %1", InstURL::HidePassword(test_url));
  948.  
  949.         string separator = (search (url, "?") == nil)? "?": "&";
  950.         url = url + separator + "alias=" + alias;
  951.     }
  952.     log_url = InstURL::HidePassword (url);
  953.     y2milestone ("zmd url: %1", log_url);
  954.  
  955.     // we're done if known and adding or unknown and deleting.
  956.     // if the status is uncertain (error),
  957.     // we proceed to be able to report it
  958.     boolean done_already = adding == IsUrlKnownToZMD (url);
  959.     if (done_already) {
  960.         y2milestone ("already in sync");
  961.         return true;
  962.     }
  963.  
  964.     string name = alias;
  965.     string owner = "zypp";    // not stype: #168739
  966.  
  967.     string command = nil;
  968.     string log_command = nil;
  969.     // be quiet, #179080
  970.     if (adding) {
  971.         const string format = "%4 --quiet service-add --type='%3' '%1' '%2' && %4 subscribe '%1'";
  972.         command = sformat (format,         url, name, owner, rug);
  973.         log_command = sformat (format, log_url, name, owner, rug);
  974.     }
  975.     else {
  976.         const string format = "%2 --quiet service-delete '%1'";
  977.         command = sformat (format, url, rug);
  978.         log_command = sformat (format, log_url, rug);
  979.     }
  980.     map ret = RunLongLog (command, log_command); // #165145
  981.     if (ret["exit"]:nil == 0) {
  982.         UpdateKnownServiceCache (adding, url);
  983.         return true;
  984.     } else {
  985.         string message = adding?
  986.         // rug is a command name
  987.         _("Your service was added successfully in YaST, but could not be synchronized with ZenWorks."):
  988.         _("Your service was deleted successfully in YaST, but could not be synchronized with ZenWorks.");
  989.         message = message + ":";
  990.         if (ret["stdout"]:[] != [])
  991.         message = message + "\n" + mergestring (ret["stdout"]:[], "\n");
  992.         if (ret["stderr"]:[] != [])
  993.         message = message + "\n" + mergestring (ret["stderr"]:[], "\n");
  994.         else if (haskey (ret, "timed_out"))
  995.         // error message
  996.         // FIXME "ms" or plural gettext
  997.         message = message + "\n" + sformat (_("Command timed out after %1 milliseconds."), ret["timed_out"]:1000);
  998.         Report::LongError (message);
  999.         return false;
  1000.     }
  1001.     }
  1002.  
  1003.     /* ZMD service name - used for starting and stopping ZMD */
  1004.     string zmd_service_name = "/etc/init.d/novell-zmd";
  1005.  
  1006.     /**
  1007.      * Start ZMD if it was not running
  1008.      * Report::Error on failure
  1009.      * @return an opaque handle
  1010.      */
  1011.     map ZMDStart () {
  1012.     map zmd_handle = $[];
  1013.     // #214588: don't complain if it is not installed
  1014.     boolean installed = (integer) SCR::Read (.target.size, zmd_service_name) > 0;
  1015.     if (! installed) {
  1016.         y2milestone ("ZMD is not installed");
  1017.         return $[ "was_running": false, "is_running": false ];
  1018.     }
  1019.  
  1020.     boolean running = CheckZMDStatus ();
  1021.     zmd_handle ["was_running"] = running;
  1022.     if (! running) {
  1023.         // Starting the service
  1024.         map zmd_start = RunLong (zmd_service_name + " start");
  1025.         y2milestone("ZMD start: %1", zmd_start);
  1026.     }
  1027.     // Checking the status after start
  1028.     running = CheckZMDStatus ();
  1029.     zmd_handle ["is_running"] = running;
  1030.     if (! running) {
  1031.         Report::Error(Message::CannotStartService(zmd_service_name));
  1032.     }
  1033.     return zmd_handle;
  1034.     }
  1035.  
  1036.  
  1037.     /**
  1038.      * @param zmd_handle what ZMDStart returned
  1039.      * @return can we work with ZMD now
  1040.      */
  1041.     boolean ZMDWorking (map zmd_handle) {
  1042.     return zmd_handle["is_running"]:false;
  1043.     }
  1044.  
  1045.     /**
  1046.      * Restore the status before ZMDStart
  1047.      * Report::Error on failure
  1048.      * @param zmd_handle what ZMDStart returned
  1049.      */
  1050.     void ZMDRestore (map zmd_handle) {
  1051.     // It was running, nothing to change
  1052.     if (zmd_handle["was_running"]:false)
  1053.         return;
  1054.     
  1055.     if (zmd_handle["is_running"]:false)
  1056.     {
  1057.         y2milestone("Stopping service ZMD %1", zmd_service_name);
  1058.         map zmd_stop = RunLong (zmd_service_name + " stop");
  1059.         y2milestone("ZMD stop: %1", zmd_stop);
  1060.         // #166900, if stop fails it means it is still busy and
  1061.         // will stop later
  1062.         if (false && zmd_stop["exit"]:nil != 0) {
  1063.         Report::Error(Message::CannotStopService(zmd_service_name));
  1064.         }
  1065.     }
  1066.     y2milestone("ZMD stopped");
  1067.     }
  1068.  
  1069.     integer source_locked = 0;
  1070.     const string source_lock_flag = "/var/lib/zypp/sources-being-processed-by-yast";
  1071.     /**
  1072.      * Start a section where other processes (such as ZMD helpers) should not
  1073.      * access the source database. #170113
  1074.      * The calls may be nested.
  1075.      */
  1076.     global void Lock () {
  1077.     if (source_locked == 0)
  1078.     {
  1079.         SCR::Write (.target.string, source_lock_flag, "bug 170113");
  1080.     }
  1081.     source_locked = source_locked + 1;
  1082.     y2milestone ("lock: %1", source_locked);
  1083.     }
  1084.  
  1085.     /**
  1086.      * Other processes may access the source database again
  1087.      */
  1088.     global void Unlock () {
  1089.     source_locked = source_locked - 1;
  1090.     if (source_locked < 0)
  1091.     {
  1092.         y2internal ("Too many unlocks!");
  1093.         source_locked = 0;
  1094.     }
  1095.     if (source_locked == 0)
  1096.     {
  1097.      SCR::Execute (.target.remove, source_lock_flag);
  1098.     }
  1099.     y2milestone ("unlock: %1", source_locked);
  1100.     }
  1101.  
  1102.  
  1103.     /**
  1104.      * Checks whether ZMD is running, starts it when isn't. Checks whether a ZYPP source is listed
  1105.      * in the ZMD services, adds one if it is missing.
  1106.      * Everything is done via the .background agent with timeout (number in seconds).
  1107.      *
  1108.      * @return boolean whether the syncing succeeds
  1109.      */
  1110.     global boolean SyncYaSTInstSourceWithZMD () {
  1111.     // Notes for maintainer:
  1112.     //     - using .background agent because of ZMD/rug call that can stuck
  1113.     //     - using 'exit $?' that work well with ZMD/rug and .background together
  1114.     //     - using TERM=dumb to suppress colors, progress bars etc. from ZMD/rug
  1115.     //     - when the function finishes, it must leave ZMD in the same status (running/stopped)
  1116.     //       as it was when the function started
  1117.     //
  1118.     // WARNIG: this function starts ZMD and expects that LIBZYPP has no lock over
  1119.     //         the RPM database. You can run it only when the RPM is not locked
  1120.     //         otherwise the ZMD will block itself!
  1121.  
  1122.     UI::OpenDialog (`VBox (
  1123.                 // popup progress information
  1124.                 `Label(SyncLabel ()), // #221250
  1125.                 `PushButton (`id (`abort), Label::AbortButton ())
  1126.                 ));
  1127.     y2milestone("--- Syncing YaST inst source with ZMD ---");
  1128.  
  1129.     Lock ();
  1130.  
  1131.     boolean ret = false;
  1132.     // Check the ZMD status and start if not running
  1133.     // If the ZMD wasn't running, we will try to start it
  1134.     // and then we will have to stop it at the end
  1135.     map zmd_handle = ZMDStart ();
  1136.     if (ZMDWorking (zmd_handle)) {
  1137.         // now we can work
  1138.  
  1139.         if (true) {
  1140.         string zmd_security_level = RugGetPref ("security-level");
  1141.         // The user has already decided to trust the inst sources
  1142.         // otherwise we would not be here. Do not ask again.
  1143.         // #182747
  1144.         RugSetPref ("security-level", "none");
  1145.  
  1146.         list<integer> src_ids = Pkg::SourceGetCurrent (false /*also disabled*/);
  1147.         ResetKnownServiceCache ();
  1148.         boolean added_ok = true;
  1149.         foreach (integer src_id, src_ids, {
  1150.             map gendata = Pkg::SourceGeneralData (src_id);
  1151.             // #219414, delete disabled ones (previous product version)
  1152.             boolean add_or_del = gendata["enabled"]:true;
  1153.             added_ok = AddOrDeleteZYPPServiceIntoZMD (src_id, add_or_del) && added_ok;
  1154.         });
  1155.         if (added_ok) {
  1156.             y2milestone("Adding ZYPP service succeeded");
  1157.         } else {
  1158.             Report::Error(Message::CannotWriteSettingsTo("ZMD"));
  1159.             y2error("Adding ZYPP service failed");
  1160.         }
  1161.         ret = added_ok;
  1162.  
  1163.         // restore
  1164.         if (zmd_security_level != nil)
  1165.         {
  1166.             RugSetPref ("security-level", zmd_security_level);
  1167.         }
  1168.         }
  1169.     }
  1170.     ZMDRestore (zmd_handle);
  1171.  
  1172.     Unlock ();
  1173.  
  1174.     y2milestone("--- Syncing finished ---");
  1175.     UI::CloseDialog();
  1176.     return ret;
  1177.     }
  1178.  
  1179.     /**
  1180.      * Sync the changed sources to ZenWorks
  1181.      * @param added_src_ids ids of sources that were added
  1182.      * @param deleted_src_ids ids of sources that were deleted
  1183.      * @return success
  1184.      */
  1185.     global boolean SyncAddedAndDeleted (list<integer> added_src_ids,
  1186.                     list<integer> deleted_src_ids) {
  1187.     boolean ret = false;
  1188.     Lock ();
  1189.     map zmd_handle = ZMDStart ();
  1190.     if (ZMDWorking (zmd_handle)) {
  1191.         ret = true;
  1192.         ResetKnownServiceCache ();
  1193.  
  1194.         string zmd_security_level = RugGetPref ("security-level");
  1195.         RugSetPref ("security-level", "none"); // #190403
  1196.  
  1197.         // first delete, then add,
  1198.         // otherwise replacing a source with itself fails: #175159
  1199.         foreach (integer id, deleted_src_ids, {
  1200.         ret = AddOrDeleteZYPPServiceIntoZMD (id, false) && ret;
  1201.         });
  1202.         foreach (integer id, added_src_ids, {
  1203.         ret = AddOrDeleteZYPPServiceIntoZMD (id, true) && ret;
  1204.         });
  1205.  
  1206.         if (zmd_security_level != nil)
  1207.         {
  1208.         RugSetPref ("security-level", zmd_security_level);
  1209.         }
  1210.     }
  1211.     ZMDRestore (zmd_handle);
  1212.     Unlock ();
  1213.     return ret;
  1214.     }
  1215.  
  1216.     /**
  1217.      * Parse a URL query (already unescaped) to a map.
  1218.      * If no equal sign, the value will be nil.
  1219.      * @param query foo=bar&baz=qux
  1220.      * @return $["foo": "bar", "baz": "qux"]
  1221.      */
  1222.     map<string, string> ParseUrlQuery (string query) {
  1223.     list<string> q_items = splitstring (query, "&");
  1224.     map<string, string> q_map = listmap (string q_item, q_items, {
  1225.         integer eqpos = search (q_item, "=");
  1226.         if (eqpos == nil)
  1227.         {
  1228.         return $[ q_item: nil ];
  1229.         }
  1230.         else
  1231.         {
  1232.         string key = substring (q_item, 0, eqpos);
  1233.         string val = substring (q_item, eqpos + 1);
  1234.         return $[ key: val ];
  1235.         }
  1236.     });
  1237.     return q_map;
  1238.     }
  1239.  
  1240.     /**
  1241.      * @param attr SourceGeneralData item
  1242.      * @return For existing sources, get a mapping from an attribute to the id
  1243.      */
  1244.     map<string,integer> get_attr_to_id (string attr) {
  1245.     list<integer> src_ids = Pkg::SourceGetCurrent (false/*enabled only?*/);
  1246.     map<string,integer> a2i = listmap (integer src_id, src_ids, {
  1247.         map gendata = Pkg::SourceGeneralData (src_id);
  1248.         string alias = gendata[attr]:"";
  1249.         return $[ alias: src_id ];
  1250.     });
  1251.     return a2i;
  1252.     }
  1253.  
  1254.     /**
  1255.      * @return For existing sources, get a mapping from the alias to the id
  1256.      */
  1257.     map<string,integer> get_alias_to_id () {
  1258.     return get_attr_to_id ("alias");
  1259.     }
  1260.  
  1261.     /**
  1262.      * @return For existing sources, get a mapping from the URL to the id
  1263.      */
  1264.     map<string,integer> get_url_to_id () {
  1265.     return get_attr_to_id ("url");
  1266.     }
  1267.  
  1268.     /**
  1269.      * Extract an alias parameter from the URL and check whether we have
  1270.      * such a source already.
  1271.      * @param url a source with an alias parameter (actually optional)
  1272.      * @param alias_to_id a premade mapping, @see get_alias_to_id
  1273.      * @return the source id or -1
  1274.      */
  1275.     integer SourceByAliasOrUrl (string url,
  1276.                 map<string,integer> alias_to_id,
  1277.                 map<string,integer> url_to_id) {
  1278.     // parse the URL
  1279.     map parsed_url = URL::Parse (url);
  1280.     y2milestone ("parsed: %1", parsed_url);
  1281.     // (reassemble and warn if it differs)
  1282.     string reassembled = URL::Build (parsed_url);
  1283.     if (url != reassembled)
  1284.     {
  1285.         y2warning ("reassembled differs: %1", reassembled);
  1286.     }
  1287.     // get the alias
  1288.     map<string, string> q_map = ParseUrlQuery (parsed_url["query"]:"");
  1289.     y2milestone ("query: %1", q_map);
  1290.     string alias = q_map["alias"]:"";
  1291.  
  1292.     // (empty: box safeguard)
  1293.     if (alias != "" && haskey (alias_to_id, alias))
  1294.     {
  1295.         return alias_to_id[alias]:-1;
  1296.     }
  1297.     // #188572: if no match by alias, try url
  1298.     return url_to_id[url]:-1;
  1299.     }
  1300.  
  1301.     /**
  1302.      * Used by registration.
  1303.      * This is really hairy because we simultaneously add them to zypp and
  1304.      * zenworks, but not if they are not signed. (thus being better than
  1305.      * inst_source)
  1306.      * @param urls update sources to add
  1307.      * @return a list of added URLs
  1308.      */
  1309.     global list<string> AddUpdateSources (list<string> urls) {
  1310.     list<string> ret = [];
  1311.  
  1312.     // prepare for lookup of known aliases
  1313.     map<string,integer> aliases = get_alias_to_id ();
  1314.     y2milestone ("alias mapping: %1", aliases);
  1315.     map<string,integer> by_url = get_url_to_id ();
  1316.     y2milestone ("url mapping: %1", by_url);
  1317.  
  1318.     Lock ();
  1319.     map zmd_handle = ZMDStart ();
  1320.     boolean zmd_working = ZMDWorking (zmd_handle);
  1321.  
  1322.     ResetKnownServiceCache ();
  1323.  
  1324.     // add the sources.
  1325.     // but do not make duplicates (#168740)
  1326.     // we detect them based on alias that suse_register gives us (#158850#c17)
  1327.     /// (but only for SLE... :-/ )
  1328.     /// Need to test what happens when we get two different update
  1329.     /// servers for SL
  1330.     /// Anyway that means only that #168740 remains unfixed for SL
  1331.     foreach (string url, urls, {
  1332.         // #180820
  1333.         boolean is_nu = search (url, "$RCE") != nil;
  1334.         y2milestone ("Should add an update source: %1", url);
  1335.         // inst_addon_update_sources also calls Pkg::SourceCreate
  1336.         // but it already skips duplicates
  1337.  
  1338.         // check if alias already there
  1339.         // if yes, delete the old one
  1340.         integer todel = SourceByAliasOrUrl (url, aliases, by_url);
  1341.         if (todel != -1)
  1342.         {
  1343.         y2milestone ("deleting the old copy, source %1", todel); 
  1344.         if (!is_nu && zmd_working)
  1345.         {
  1346.             AddOrDeleteZYPPServiceIntoZMD (todel, false);
  1347.         }
  1348.         Pkg::SourceDelete (todel);
  1349.         }
  1350.         // then add the new one
  1351.         y2milestone ("adding source");
  1352.         integer toadd = Pkg::SourceCreate (url, "/");
  1353.         // and add to zenworks too, but only if it succeeded here (#180820)
  1354.         if (toadd != -1 && toadd != nil)
  1355.         {
  1356.         ret = add (ret, url); // #180820#c26
  1357.         if (!is_nu && zmd_working)
  1358.         {
  1359.             AddOrDeleteZYPPServiceIntoZMD (toadd, true);
  1360.         }
  1361.         }
  1362.     });
  1363.  
  1364.     ZMDRestore (zmd_handle);
  1365.     Unlock ();
  1366.  
  1367.     return ret;
  1368.     }
  1369.  
  1370.     // adding YaST installation source into the ZMD
  1371.     // ------------------------------------------------------------------------------------------------------
  1372.  
  1373. /**
  1374.  *
  1375.  */
  1376. global boolean AskForCD (string message) {
  1377.     list<map<string,any> > cdroms = (list<map<string,any> >)
  1378.     SCR::Read (.probe.cdrom);
  1379.     boolean multiple_drives = size (cdroms) > 1;
  1380.     term drives_sel = `Empty ();
  1381.     list<string> devices = maplist (map<string,any> d, cdroms, {
  1382.     return d["dev_name"]:"";
  1383.     });
  1384.     if (multiple_drives)
  1385.     {
  1386.     drives_sel = `SelectionBox (`id (`drives), _("&Drive to eject"),
  1387.          devices);
  1388.     }
  1389.     term contents = `HBox (`HSpacing (1), `VBox (
  1390.     `VSpacing (0.5),
  1391.     `Label (message),
  1392.     `VSpacing (0.5),
  1393.     drives_sel,
  1394.     `VSpacing (0.5),
  1395.     `HBox (
  1396.         `HStretch (),
  1397.         `HWeight (1, `PushButton (`id (`cont), Label::ContinueButton ())),
  1398.         `HWeight (1, `PushButton (`id (`cancel), Label::CancelButton ())),
  1399.         `HWeight (1, `PushButton (`id (`eject), _("&Eject"))),
  1400.         `HStretch ()
  1401.     ),
  1402.     `VSpacing (0.5)
  1403.     ), `HSpacing (1));
  1404.     UI::OpenDialog (contents);
  1405.     if (multiple_drives)
  1406.     UI::ChangeWidget (`id (`drives), `CurrentItem, devices[0]:"");
  1407.     UI::SetFocus (`id (`cont));
  1408.     symbol ret = nil;
  1409.     while (true)
  1410.     {
  1411.     ret = (symbol)UI::UserInput ();
  1412.     if (ret == `cont || ret == `cancel)
  1413.     {
  1414.         break;
  1415.     }
  1416.     if (ret == `eject)
  1417.     {
  1418.         if (multiple_drives)
  1419.         {
  1420.         string device = (string)UI::QueryWidget (`id (`drives), `Value);
  1421.         SCR::Execute (.target.bash, sformat ("/bin/eject %1", device));
  1422.         }
  1423.         else
  1424.         {
  1425.         SCR::Execute (.target.bash, sformat ("/bin/eject %1",
  1426.             devices[0]:""));
  1427.         }
  1428.     }
  1429.     ret = nil;
  1430.     }
  1431.     UI::CloseDialog ();
  1432.     return ret == `cont;
  1433. }
  1434.  
  1435. /**
  1436.  * Function returns the partiton name which is used as a source for the installation
  1437.  * (IF any partition is used as a source for installation, of course).
  1438.  * Otherwise it returns an empty string "". See bugzilla #208222 for more information.
  1439.  *
  1440.  * @return string partition name
  1441.  */
  1442. global string InstallationSourceOnPartition () {
  1443.     string install_mode = Linuxrc::InstallInf ("InstMode");
  1444.  
  1445.     // Hard Disk is used for the installation
  1446.     if (install_mode == "hd") {
  1447.     string install_partition = Linuxrc::InstallInf ("Partition");
  1448.  
  1449.     // No partiton is defined - error
  1450.     if (install_partition == "" || install_partition == nil) {
  1451.         y2error ("Despite the fact that the install-mode is '%1', install-partition is '%2'",
  1452.         install_mode, install_partition
  1453.         );
  1454.         return "";
  1455.     } else {
  1456.         return install_partition;
  1457.     }
  1458.     } else {
  1459.     return "";
  1460.     }
  1461. }
  1462.  
  1463. /* EOF */
  1464. }
  1465.