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 / AddOnProduct.ycp < prev    next >
Text File  |  2006-11-29  |  26KB  |  945 lines

  1. /**
  2.  * File:
  3.  *    AddOnProduct.ycp
  4.  *
  5.  * Module:
  6.  *    AddOnProduct
  7.  *
  8.  * Summary:
  9.  *    This module provides integration of the add-on products
  10.  *
  11.  * Authors:
  12.  *    Jiri Srain<jsrain@suse.de>
  13.  */
  14.  
  15. {
  16. module "AddOnProduct";
  17.  
  18. textdomain "installation";
  19.  
  20. import "Label";
  21. import "Mode";
  22. import "ProductControl";
  23. import "ProductFeatures";
  24. import "Report";
  25. import "XML";
  26. import "Wizard";
  27. import "FileUtils";
  28. import "Language";
  29. import "Popup";
  30. import "InstShowInfo";
  31. import "ProductLicense";
  32.  
  33. // variables for installation with product
  34. /**
  35.  * ID for cache in the inst-sys
  36.  */
  37. integer src_cache_id = -1;
  38.  
  39. /**
  40.  * System proposals have already been prepared for merging?
  41.  */
  42. boolean system_proposals_prepared = false;
  43.  
  44. /**
  45.  * System workflows have already been prepared for merging?
  46.  */
  47. boolean system_workflows_prepared = false;
  48.  
  49. /**
  50.  * List of all selected sources
  51.  */
  52. global list<map<string,any> > add_on_products = [];
  53.  
  54. /**
  55.  * ID of currently added source for the add-on product
  56.  */
  57. global integer src_id  = nil;
  58.  
  59. // for the add-on product workflow - needed for dialog skipping
  60. /**
  61.  * return value of last step in the product adding workflow
  62.  */
  63. global symbol last_ret = nil;
  64.  
  65. /**
  66.  * List of used control files, to be copied to target system
  67.  */
  68. global list<string> control_files_to_add = [];
  69.  
  70. /**
  71.  * Items if add-on product to be performed at the start of inst_finish
  72.  */
  73. global list<string> finish_steps_before_chroot = [];
  74.  
  75. /**
  76.  * Items of add-on product to be performed after swictching to chroot
  77.  */
  78. global list<string> finish_steps_after_chroot = [];
  79.  
  80. /**
  81.  * Items of add-on product to be performed before unmounting disks
  82.  */
  83. global list<string> finish_steps_before_umount = [];
  84.  
  85. global boolean modified = false;
  86.  
  87. global list<integer> mode_config_sources = [];
  88.  
  89.  
  90.  
  91. /**
  92.  * Adapts the inst-sys from the tarball
  93.  * @param filename string the filename with the tarball to use to the update
  94.  * @return boolean true on success
  95.  */
  96. global boolean UpdateInstSys (string filename) {
  97.     src_cache_id = src_cache_id + 1;
  98.     string tmpdir = (string)SCR::Read (.target.tmpdir);
  99.     tmpdir = sformat ("%1/%2", tmpdir, src_cache_id);
  100.     map out = (map)SCR::Execute (.target.bash_output, sformat ("
  101. /bin/mkdir %1;
  102. cd %1;
  103. /bin/tar -xvf %2;
  104. /sbin/adddir %1 /;
  105. ", tmpdir, filename));
  106.     if (out["exit"]:0 != 0)
  107.     {
  108.     y2error ("Including installation image failed: %1", out);
  109.     return false;
  110.     }
  111.     y2milestone ("Including installation image succeeded");
  112.     return true;
  113. }
  114.  
  115. /**
  116.  * Check all proposals, split those ones which have multiple modes or
  117.  * architectures or stages into multiple proposals
  118.  * @param proposals a list of proposals
  119.  * @return a list of updated proposals
  120.  */
  121. list<map> PrepareProposals(list<map> proposals) {
  122.     list<map> new_proposals = [];
  123.     foreach (map p, proposals, {
  124.     string mode = p["mode"]:"";
  125.     list<string> modes = splitstring (mode, ",");
  126.     if (size(modes) == 0)
  127.         modes = [""];
  128.     foreach (string m, modes, {
  129.         map mp = p;
  130.         mp["mode"] = m;
  131.         string arch = p["archs"]:"";
  132.         list<string> archs = splitstring (arch, ",");
  133.         if (size(archs) == 0)
  134.         archs = [""];
  135.         foreach (string a, archs, {
  136.         map amp = mp;
  137.         amp["archs"] = a;
  138.         string stage = amp["stage"]:"";
  139.         list<string> stages = splitstring(stage, ",");
  140.         if (size (stages) == 0)
  141.             stages = [""];
  142.         foreach (string s, stages, {
  143.             map samp = amp;
  144.             samp["stage"] = s;
  145.             new_proposals = add (new_proposals, samp);
  146.         });
  147.         });
  148.     });
  149.     });
  150.     return new_proposals;
  151. }
  152.  
  153. /**
  154.  * Check all proposals, split those ones which have multiple modes or
  155.  * architectures or stages into multiple proposals.
  156.  * Works with base product proposals.
  157.  */
  158. void PrepareSystemProposals() {
  159.     if (system_proposals_prepared)
  160.     return;
  161.     ProductControl::proposals = PrepareProposals(ProductControl::proposals);
  162.     system_proposals_prepared = true;
  163. }
  164.  
  165. /**
  166.  * Check all workflows, split those ones which have multiple modes or
  167.  * architectures or stages into multiple workflows
  168.  * @param workflows a list of workflows
  169.  * @return a list of updated workflows
  170.  */
  171. list<map> PrepareWorkflows(list<map> workflows) {
  172.     list<map> new_workflows = [];
  173.     foreach (map w, workflows, {
  174.     string mode = w["mode"]:"";
  175.     list<string> modes = splitstring (mode, ",");
  176.     if (size(modes) == 0)
  177.         modes = [""];
  178.     foreach (string m, modes, {
  179.         map mw = w;
  180.         mw["mode"] = m;
  181.         mw["defaults"] = mw["defaults"]:$[];
  182.         string arch = mw["defaults", "archs"]:"";
  183.         list<string> archs = splitstring (arch, ",");
  184.         if (size(archs) == 0)
  185.         archs = [""];
  186.         foreach (string a, archs, {
  187.         map amw = mw;
  188.         amw["defaults", "archs"] = a;
  189.         string stage = amw["stage"]:"";
  190.         list<string> stages = splitstring(stage, ",");
  191.         if (size (stages) == 0)
  192.             stages = [""];
  193.         foreach (string s, stages, {
  194.             map samw = amw;
  195.             samw["stage"] = s;
  196.             new_workflows = add (new_workflows, samw);
  197.         });
  198.         });
  199.     });
  200.     });
  201.     return new_workflows;
  202. }
  203.  
  204. /**
  205.  * Check all workflows, split those ones which have multiple modes or
  206.  * architectures or stages into multiple worlflows.
  207.  * Works with base product workflows.
  208.  */
  209. void PrepareSystemWorkflows() {
  210.     if (system_workflows_prepared)
  211.     return;
  212.     ProductControl::workflows = PrepareWorkflows(ProductControl::workflows);
  213.     system_workflows_prepared = true;
  214. }
  215.  
  216. /**
  217.  * Replace a module in a proposal with a set of other modules
  218.  * @param proposal a map describing the proposal
  219.  * @param old string the old item to be replaced
  220.  * @param new a list of items to be put into instead of the old one
  221.  * @return a map with the updated proposal
  222.  */
  223. map ReplaceProposalModule(map proposal, string old, list<string> new) {
  224.     boolean found = false;
  225.     list<list> modules = maplist (any m, proposal["proposal_modules"]:[], {
  226.     if ((is (m, string) && (string)m == old)
  227.         || is (m, map) && ((map)m)["name"]:"" == old)
  228.     {
  229.         found = true;
  230.         if (is (m, map))
  231.         {
  232.         return maplist (string it, new, {
  233.             return union ((map)m, $[ "name" : it ]);
  234.         });
  235.         }
  236.         else
  237.         {
  238.         return new;
  239.         }
  240.     }
  241.     else
  242.     {
  243.         return [m];
  244.     }
  245.     });
  246.     if (! found)
  247.     y2internal ("Replace/Remove proposal item %1 not found", old);
  248.     proposal["proposal_modules"] = flatten(modules);
  249.     if (haskey (proposal, "proposal_tabs"))
  250.     {
  251.     proposal["proposal_tabs"] = maplist (map tab,
  252.         proposal["proposal_tabs"]:[],
  253.     {
  254.         list<list<string> > modules = maplist (string m,
  255.         tab["proposal_modules"]:[],
  256.         {
  257.         if (m == old)
  258.             return new;
  259.         else
  260.             return [ m ];
  261.         });
  262.         tab["proposal_modules"] = flatten(modules);
  263.         return tab;
  264.     });
  265.     }
  266.     return proposal;
  267. }
  268.  
  269.  
  270. /**
  271.  * Merge add-on proposal to a base proposal
  272.  * @param base map the base product proposal
  273.  * @param addon map the proposal of the addon productA
  274.  * @param prod_name a name of the add-on product
  275.  * @return map merged proposals
  276.  */
  277. map MergeProposal(map base, map addon, string prod_name, string domain)
  278. {
  279.     list<string> appends = addon["append_modules"]:[];
  280.     list<string> removes = addon["remove_modules"]:[];
  281.     map<string,list<string> > replaces = listmap(map a,
  282.     addon["replace_modules"]:[],
  283.     {
  284.     string old = a["replace"]:"";
  285.     list<string> new = a["modules"]:[];
  286.     return $[ old : new ];
  287.     });
  288.     if (size (removes) > 0)
  289.     {
  290.         foreach (string r, removes, {
  291.         base = ReplaceProposalModule (base, r, []);
  292.     });
  293.     }
  294.     if (size (replaces) > 0)
  295.     {
  296.     foreach (string old, list<string> new, replaces, {
  297.         base = ReplaceProposalModule (base, old, new);
  298.     });
  299.     }
  300.     if (size (appends) > 0)
  301.     {
  302.     boolean as_map = false;
  303.     list<any> append2 = appends;
  304.     if (is (base["proposal_modules", 0]:nil, map))
  305.     {
  306.         append2 = maplist (string m, appends, {
  307.         return $[ "name" : m, "presentation_order" : 9999 ];
  308.         });
  309.     }
  310.     base["proposal_modules"] = merge (base["proposal_modules"]:[], append2);
  311.     if (haskey (base, "proposal_tabs"))
  312.     {
  313.         map new_tab = $[
  314.         "label" : prod_name,
  315.         "proposal_modules" : appends,
  316.         "textdomain" : domain,
  317.         ];
  318.         base["proposal_tabs"] = add (base["proposal_tabs"]:[], new_tab);
  319.     }
  320.     }
  321.     if (addon["enable_skip"]:"yes" == "no")
  322.     base["enable_skip"] = "no";
  323.     return base;
  324. }
  325.  
  326. /**
  327.  * Update system proposals according to proposal update metadata
  328.  * @param proposals a list of update proposals
  329.  * @param prod_name string the product name (used in case of tabs)
  330.  * @param domain string the text domain (for translations)
  331.  * @return boolean true on success
  332.  */
  333. boolean UpdateProposals(list<map> proposals, string prod_name, string domain) {
  334.     foreach (map proposal, proposals, {
  335.     string name = proposal["name"]:"";
  336.     string stage = proposal["stage"]:"";
  337.     string mode = proposal["mode"]:"";
  338.     string arch = proposal["archs"]:"";
  339.     boolean found = false;
  340.     list<map> new_proposals = [];
  341.     map arch_all_prop = $[];
  342.     foreach (map p, ProductControl::proposals, {
  343.         if (p["stage"]:"" != stage || p["mode"]:"" != mode
  344.             || p["name"]:"" != name)
  345.         {
  346.         new_proposals = add (new_proposals, p);
  347.         continue;
  348.         }
  349.         if (p["archs"]:"" == arch || arch == "" || arch == "all")
  350.         {
  351.         p = MergeProposal (p, proposal, prod_name, domain);
  352.         found = true;
  353.         }
  354.         else if (p["archs"]:"" == "" || p["archs"]:"" == "all")
  355.         {
  356.         arch_all_prop = p;
  357.         }
  358.         new_proposals = add (new_proposals, p);
  359.     });
  360.     if (! found)
  361.     {
  362.         if (arch_all_prop != $[])
  363.         {
  364.         arch_all_prop["archs"] = arch;
  365.         proposal = MergeProposal (arch_all_prop, proposal,
  366.             prod_name, domain);
  367.         }
  368.         else // completly new proposal
  369.         {
  370.         proposal["textdomain"] = domain;
  371.         }
  372.         new_proposals = add (new_proposals, proposal);
  373.     }
  374.     ProductControl::proposals = new_proposals;
  375.     });
  376.     return true;
  377. }
  378.  
  379. /**
  380.  * Replace a module in a workflow with a set of other modules
  381.  * @param workflow a map describing the workflow
  382.  * @param old string the old item to be replaced
  383.  * @param new a list of items to be put into instead of the old one
  384.  * @param domain string a text domain
  385.  * @param keep boolean true to keep original one (and just insert before)
  386.  * @return a map with the updated workflow
  387.  */
  388. map ReplaceWorkflowModule(map workflow, string old, list<map> new,
  389.     string domain, boolean keep)
  390. {
  391.     boolean found = false;
  392.     list<list<map> > modules = maplist (map m, workflow["modules"]:[], {
  393.     if (m["name"]:"" == old)
  394.     {
  395.         list<map> new_list = maplist (map n, new, {
  396.         n["textdomain"] = domain;
  397.         return n;
  398.         });
  399.         found = true;
  400.         if (keep)
  401.         new_list = add (new_list, m);
  402.         return new_list;
  403.     }
  404.     else
  405.     {
  406.         return [m];
  407.     }
  408.     });
  409.     if (! found)
  410.     y2internal ("Insert/Replace/Remove workflow module %1 not found", old);
  411.     workflow["modules"] = flatten(modules);
  412.     return workflow;
  413. }
  414.  
  415.  
  416. /**
  417.  * Merge add-on workflow to a base workflow
  418.  * @param base map the base product workflow
  419.  * @param addon map the workflow of the addon product
  420.  * @param prod_name a name of the add-on product
  421.  * @return map merged workflows
  422.  */
  423. map MergeWorkflow(map base, map addon, string prod_name, string domain)
  424. {
  425.     list<map> appends = addon["append_modules"]:[];
  426.     list<string> removes = addon["remove_modules"]:[];
  427.     map<string,list<map> > inserts = listmap (map i,
  428.     addon["insert_modules"]:[],
  429.     {
  430.     string before = i["before"]:"";
  431.     list<map> new = i["modules"]:[];
  432.     return $[ before : new ];
  433.     });
  434.     map<string,list<map> > replaces = listmap(map a,
  435.     addon["replace_modules"]:[],
  436.     {
  437.     string old = a["replace"]:"";
  438.     list<map> new = a["modules"]:[];
  439.     return $[ old : new ];
  440.     });
  441.     if (size (removes) > 0)
  442.     {
  443.         foreach (string r, removes, {
  444.         base = ReplaceWorkflowModule (base, r, [], domain, false);
  445.     });
  446.     }
  447.     if (size (replaces) > 0)
  448.     {
  449.     foreach (string old, list<map> new, replaces, {
  450.         base = ReplaceWorkflowModule (base, old, new, domain, false);
  451.     });
  452.     }
  453.     if (size (inserts) > 0)
  454.     {
  455.     foreach (string old, list<map> new, inserts, {
  456.         base = ReplaceWorkflowModule (base, old, new, domain, true);
  457.     });
  458.     }
  459.     if (size (appends) > 0)
  460.     {
  461.     foreach (map new, appends, {
  462.         new["textdomain"] = domain;
  463.         base["modules"] = add (base["modules"]:[], new);
  464.     });
  465.     }
  466.     return base;
  467. }
  468.  
  469. /**
  470.  * Update system workflows according to workflow update metadata
  471.  * @param workflows a list of update workflows
  472.  * @param prod_name string the product name (used in case of tabs)
  473.  * @param domain string the text domain (for translations)
  474.  * @return boolean true on success
  475.  */
  476. boolean UpdateWorkflows(list<map> workflows, string prod_name, string domain) {
  477.     foreach (map workflow, workflows, {
  478.     string stage = workflow["stage"]:"";
  479.     string mode = workflow["mode"]:"";
  480.     string arch = workflow["archs"]:"";
  481.     boolean found = false;
  482.     list<map> new_workflows = [];
  483.     map arch_all_wf = $[];
  484.     foreach (map w, ProductControl::workflows, {
  485.         if (w["stage"]:"" != stage || w["mode"]:"" != mode)
  486.         {
  487.         new_workflows = add (new_workflows, w);
  488.         continue;
  489.         }
  490.         if (w["defaults", "archs"]:"" == arch
  491.         || arch == "" || arch == "all")
  492.         {
  493.         w = MergeWorkflow (w, workflow, prod_name, domain);
  494.         found = true;
  495.         }
  496.         else if (w["defaults", "archs"]:"" == ""
  497.         || w["default", "archs"]:"" == "all")
  498.         {
  499.         arch_all_wf = w;
  500.         }
  501.         new_workflows = add (new_workflows, w);
  502.     });
  503.     if (! found)
  504.     {
  505.         if (arch_all_wf != $[])
  506.         {
  507.         arch_all_wf["defaults", "archs"] = arch;
  508.         workflow = MergeWorkflow (arch_all_wf, workflow,
  509.             prod_name, domain);
  510.         }
  511.         else // completly new workflow
  512.         {
  513.         workflow["textdomain"] = domain;
  514.         workflow["modules"] = maplist (map mod, workflow["modules"]:[], {
  515.             mod["textdomain"] = domain;
  516.             return mod;
  517.         });
  518.         }
  519.         new_workflows = add (new_workflows, workflow);
  520.     }
  521.     ProductControl::workflows = new_workflows;
  522.     });
  523.     return true;
  524. }
  525.  
  526. /**
  527.  * Redraw the wizard steps bar
  528.  * @return boolean true on success
  529.  */
  530. boolean RedrawWizardSteps () {
  531. // FIXME following code copy-pasted from clients/installation.ycp
  532.     y2milestone( "Retranslating messages" );
  533.  
  534.     // Make sure the labels for default function keys are retranslated, too.
  535.     // Using Label::DefaultFunctionKeyMap() from Label module.
  536.     UI::SetFunctionKeys( Label::DefaultFunctionKeyMap() );
  537.  
  538.     // Activate language changes on static part of wizard dialog
  539.  
  540.     ProductControl::RetranslateWizardSteps();
  541.     Wizard::RetranslateButtons();
  542.     Wizard::SetFocusToNextButton();
  543. // FIXME end of copy-paste
  544.     return true;
  545. }
  546.  
  547. /**
  548.  * Add specified steps to inst_finish
  549.  * Just modifies internal variables, inst_finish grabs them itself
  550.  * @param additional_steps a map specifying the steps to be added
  551.  * @return boolean true on success
  552.  */
  553. boolean UpdateInstFinish (map<string,list<string> > additional_steps) {
  554.     list<string> before_chroot = additional_steps["before_chroot"]:[];
  555.     list<string> after_chroot = additional_steps["after_chroot"]:[];
  556.     list<string> before_umount = additional_steps["before_umount"]:[];
  557.     finish_steps_before_chroot = (list<string>)
  558.     merge (finish_steps_before_chroot, before_chroot);
  559.     finish_steps_after_chroot = (list<string>)
  560.     merge (finish_steps_after_chroot, after_chroot);
  561.     finish_steps_before_umount = (list<string>)
  562.     merge (finish_steps_before_umount, before_umount);
  563.     return true;
  564. }
  565.  
  566. /**
  567.  * Adapts the installation workflow according to specified XML file
  568.  * @param update_file a map containing the add-on product control file
  569.  * @param name string the name of the add-on product
  570.  * @param domain string the text domain for the add-on product
  571.  * @return boolean true on success
  572.  */
  573. boolean UpdateInstallation (map update_file, string name, string domain) {
  574.     PrepareSystemProposals ();
  575.     PrepareSystemWorkflows ();
  576.     list<map> proposals = update_file["proposals"]:[];
  577.     proposals = PrepareProposals (proposals);
  578.     UpdateProposals (proposals, name, domain);
  579.     list<map> workflows = update_file["workflows"]:[];
  580.     workflows = PrepareWorkflows (workflows);
  581.     UpdateWorkflows (workflows, name, domain);
  582.     return true;
  583. }
  584.  
  585. /**
  586.  * Add new defined proposal to the list of system proposals
  587.  * @param proposals a lsit of proposals to be added
  588.  * @return boolean true on success
  589.  */
  590. boolean AddNewProposals (list<map> proposals) {
  591.     list<string> forbidden = maplist (map p, ProductControl::proposals, {
  592.     return p["name"]:"";
  593.     });
  594.     forbidden = toset (forbidden);
  595.     foreach (map proposal, proposals, {
  596.     if (! contains (forbidden, proposal["name"]:""))
  597.     {
  598.         y2milestone ("Adding new proposal %1", proposal["name"]:"");
  599.         ProductControl::proposals = add (ProductControl::proposals, proposal);
  600.     }
  601.     });
  602.     return true;
  603. }
  604.  
  605. /**
  606.  * Replace workflows for 2nd stage of installation
  607.  * @param workflows a list of the workflows
  608.  * @return boolean true on success
  609.  */
  610. boolean Replaceworkflows (list<map> workflows) {
  611.     workflows = PrepareWorkflows (workflows);
  612.     workflows = filter (map w, workflows, {
  613.     if (w["stage"]:"" == "initial")
  614.     {
  615.         y2error ("Attempting to replace 1st stage workflow. This is not possible");
  616.         return false;
  617.     }
  618.     return true;
  619.     });
  620.     map<string,map<string, boolean> > sm = $[];
  621.     foreach (map w, workflows, {
  622.     sm[w["stage"]:""] = sm[w["stage"]:""]:$[];
  623.     sm[w["stage"]:"", w["mode"]:""] = true;
  624.     return [w["stage"]:"", w["mode"]:""];
  625.     });
  626.     y2milestone ("Existing replace workflows: %1", sm);
  627.     y2milestone ("Workflows before filtering: %1", size (ProductControl::workflows));
  628.     ProductControl::workflows = filter (map w, ProductControl::workflows, {
  629.     return ! sm[w["stage"]:"", w["mode"]:""]:false;
  630.     });
  631.     y2milestone ("Workflows after filtering: %1", size (ProductControl::workflows));
  632.     ProductControl::workflows = (list<map>)merge (ProductControl::workflows, workflows);
  633.     return true;
  634. }
  635.  
  636. /**
  637.  * Update product options
  638.  * @param update_file a map containing update control file
  639.  * @return boolean true on success
  640.  */
  641. boolean UpdateProductInfo(map update_file) {
  642.     foreach (string section, ["globals", "software", "partitioning", "network"],
  643.     {
  644.     map<string,any> sect = ProductFeatures::GetSection(section);
  645.     map<string,any> addon = update_file[section]:$[];
  646.     sect = (map<string,any>)union (sect, addon);
  647.     ProductFeatures::SetSection(section, sect);
  648.     });
  649.     list<string> addon_clone = update_file["clone_modules"]:[];
  650.     ProductControl::clone_modules
  651.     = (list<string>)merge (ProductControl::clone_modules, addon_clone);
  652.     return true;
  653. }
  654.  
  655. /**
  656.  * Remove the /y2update directory from the system
  657.  */
  658. void CleanY2Update() {
  659.     SCR::Execute (.target.bash, "/bin/rm -rf /y2update");
  660. }
  661.  
  662. /**
  663.  * Show /media.1/info.txt file in a pop-up message if such file exists.
  664.  * Show license if such exists and return whether users accepts it.
  665.  * Returns 'nil' when did not succed.
  666.  *
  667.  * @return boolean whether the license has been accepted
  668.  */
  669. global boolean AcceptedLicenseAndInfoFile (integer src_id) {
  670.     symbol ret = ProductLicense::AskAddOnLicenseAgreement (src_id);
  671.     if (ret == nil)
  672.     return nil;
  673.     else if (ret == `abort || ret == `back)
  674.     {
  675.     y2milestone ("License confirmation failed");
  676.     return false;
  677.     }
  678.     return true;
  679. }
  680.  
  681. /**
  682.  * Do installation of the add-on product within an installed system
  683.  * srcid is got via AddOnProduct::src_id
  684.  *
  685.  * @param string src_id
  686.  * @return symbol the result symbol from wizard sequencer
  687.  */
  688. global symbol DoInstall() {
  689.     // Display /media.1/info.txt if such file exists
  690.     // Display license and wait for agreement
  691.     // Not needed here, license already shown in the workflow
  692.     /*
  693.     boolean license_ret = AcceptedLicenseAndInfoFile(src_id);
  694.     if (license_ret != true) {
  695.     y2milestone("Removing the current source ID %1", src_id);
  696.     Pkg::SourceDelete(src_id);
  697.     return nil;
  698.     }
  699.     */
  700.  
  701.     string control = Pkg::SourceProvideOptionalFile (src_id, 1, "/installation.xml");
  702.     // Fallback -- Source didn't provide needed controll file
  703.     // Handling as it was an installation source
  704.     if (control == nil)
  705.     {
  706.     y2milestone("File /installation.xml not found, running sw_single for this source");
  707.     WFM::CallFunction ("sw_single", []);
  708.  
  709.     return nil;
  710.     }
  711.  
  712.     // copy the control file to local filesystem - in case of media release
  713.     string tmp = (string)SCR::Read (.target.tmpdir);
  714.     tmp = tmp + "/installation.xml";
  715.     SCR::Execute (.target.bash, sformat ("/bin/cp %1 %2", control, tmp));
  716.     control = tmp;
  717.  
  718.     string binaries = Pkg::SourceProvideOptionalFile (src_id, 1, "/y2update.tgz");
  719.     // File /y2update.tgz exists
  720.     if (binaries != nil)
  721.     {
  722.     // Try to extract files from the archive
  723.     map out = (map)SCR::Execute (.target.bash_output, sformat ("
  724. test -d /y2update && rm -rf /y2update;
  725. /bin/mkdir -p /y2update/all;
  726. cd /y2update/all;
  727. /bin/tar -xvf %1;
  728. cd /y2update;
  729. ln -s all/usr/share/YaST2/* .;
  730. ln -s all/usr/lib/YaST2/* .;
  731. ", binaries));
  732.  
  733.     // Failed
  734.     if (out["exit"]:0 != 0)
  735.     {
  736.         // error report
  737.         Report::Error (_("An error occurred while preparing the installation system."));
  738.         CleanY2Update();
  739.         return nil;
  740.     }
  741.     }
  742.     else
  743.     {
  744.     y2milestone("File /y2update.tgz not provided");
  745.     }
  746.  
  747.     // set control file
  748.     ProductControl::custom_control_file = control;
  749.     if (!ProductControl::Init())
  750.     {
  751.     // error report
  752.     Report::Error (sformat (_("Control file %1 not found on media."),
  753.         control));
  754.     CleanY2Update();
  755.     return nil;
  756.     }
  757.     // start workflow
  758.     Wizard::OpenNextBackStepsDialog();
  759.     // dialog caption
  760.     Wizard::SetContents(_("Initializing..."), `Empty (), "", false, false);
  761.     list<map> stage_mode = [$["stage": "normal",  "mode": "installation" ]];
  762.     ProductControl::AddWizardSteps(stage_mode);
  763.     Mode::SetMode ("installation");
  764.     symbol ret = ProductControl::Run();
  765.     UI::CloseDialog();
  766.     CleanY2Update();
  767.     return ret;
  768. }
  769.  
  770. /**
  771.  * Integrate the changes in the workflow
  772.  * @param filename string filename of the control file (local filename)
  773.  * @return boolean true on success
  774.  */
  775. global boolean WFIntegrate (string filename) {
  776.     map update_file = XML::XMLToYCPFile (filename);
  777.     string name = update_file["display_name"]:"";
  778.     boolean ret = UpdateInstallation (update_file["update"]:$[], name,
  779.         update_file["textdomain"]:"control");
  780.     if (! ret)
  781.     {
  782.     y2error ("Failed to udpate installation workflow");
  783.     return false;
  784.     }
  785.     if (! UpdateProductInfo (update_file))
  786.     {
  787.     y2error ("Failed to set product options");
  788.     return false;
  789.     }
  790.     if (! AddNewProposals (update_file["proposals"]:[]))
  791.     {
  792.     y2error ("Failed to add new proposals");
  793.     return false;
  794.     }
  795.     if (! Replaceworkflows (update_file["workflows"]:[]))
  796.     {
  797.     y2error ("Failed to replace workflows");
  798.     return false;
  799.     }
  800.     if (! RedrawWizardSteps ())
  801.     {
  802.     y2error ("Redrawing the wizard steps failed");
  803.     return false;
  804.     }
  805.     if (! UpdateInstFinish (update_file["update", "inst_finish"]:$[]))
  806.     {
  807.     y2error ("Adding inst_finish steps failed");
  808.     return false;
  809.     }
  810.     return true;
  811. }
  812.  
  813. /**
  814.  * Integrate the add-on product to the installation workflow, including
  815.  * preparations for 2nd stage and inst-sys update
  816.  * @param srcid integer the source ID of the installation source
  817.  * @return boolean true on success
  818.  */
  819. global boolean Integrate (integer srcid) {
  820.     y2milestone ("Integrating source %1", srcid);
  821.  
  822.     string filename = Pkg::SourceProvideOptionalFile (srcid, 1, "/installation.xml");
  823.     if (filename == nil)
  824.     {
  825.     y2milestone ("Add-on product control file not found, not touching the workglow");
  826.     }
  827.     else
  828.     {
  829.     if (! WFIntegrate (filename))
  830.     {
  831.         y2error ("Workflow update failed");
  832.         return false;
  833.     }
  834.     string tmpdir = (string)SCR::Read (.target.tmpdir) + "/control_files";
  835.     map out = (map)SCR::Execute (.target.bash_output, sformat ("
  836. test -d %1 || /bin/mkdir %1;
  837. /bin/cp %2 %1/%3.xml;
  838. ", tmpdir, filename, srcid));
  839.     if (out["exit"]:-1 != 0)
  840.     {
  841.         y2error ("Error occurred while copying control file: %1", out);
  842.     }
  843.     control_files_to_add = add (control_files_to_add, sformat ("%1.xml", srcid));
  844.     }
  845.     string y2update = Pkg::SourceProvideOptionalFile (srcid, 1, "/y2update.tgz");
  846.     if (y2update == nil)
  847.     {
  848.     y2milestone ("No YaST update found on the media");
  849.     }
  850.     else
  851.     {
  852.     UpdateInstSys (y2update);
  853.     }
  854.     return true;
  855. }
  856.  
  857. global boolean CheckProductDependencies (list<string> products) {
  858. // TODO check the dependencies of the product
  859.     return true;
  860. }
  861.  
  862. /**
  863.  * Auto-integrate add-on products in specified file
  864.  * @param filelist string a file containing a list of add-on products to integrate
  865.  * @return boolean true on exit
  866.  */
  867. global boolean AddPreselectedAddOnProducts(string filelist) {
  868.     if (filelist == nil)
  869.     {
  870.     y2milestone ("No add-on products defined on the media");
  871.     return true;
  872.     }
  873.     list<string> products = splitstring ((string)SCR::Read (.target.string, filelist), "\r\n");
  874.     foreach (string p, products, {
  875.     if (p == "")
  876.         return;
  877.     list<string> elements = splitstring (p, " \t");
  878.     elements = filter (string e, elements, { return e != ""; });
  879.     string url = elements[0]:"";
  880.     string pth = elements[1]:"/";
  881.     elements = remove (elements, 0);
  882.     elements = remove (elements, 0);
  883.     integer src = Pkg::SourceCreate (url, pth);
  884.     if (! AcceptedLicenseAndInfoFile(src))
  885.     {
  886.         Pkg::SourceDelete (src);
  887.         return;
  888.     }
  889.     Integrate (src);
  890.     if (size (elements) > 0)
  891.     {
  892.         foreach (string e, elements, {
  893.         Pkg::ResolvableInstall (e, `product);
  894.         });
  895.     }
  896.     else
  897.     {
  898.         list<map<string,any> > products = Pkg::ResolvableProperties ("", `product, "");
  899.         products = filter (map<string,any> p, products, {
  900.         return p["source"]:-1 == src;
  901.         });
  902.         foreach (map<string,any> p, products, {
  903.         Pkg::ResolvableInstall (p["name"]:"", `product);
  904.         });
  905.     }
  906.     });
  907. }
  908.  
  909. global map Export () {
  910.     list<map<string,any> > exp = maplist (map<string,any> p, add_on_products, {
  911.     if (haskey (p, "media"))
  912.         p = remove (p, "media");
  913.     return p;
  914.     });
  915.     return $[
  916.     "add_on_products" : exp,
  917.     ];
  918. }
  919.  
  920. global boolean Import (map settings) {
  921.     add_on_products = settings["add_on_products"]:[];
  922.     modified = false;
  923.     if (Mode::config ())
  924.     {
  925.     foreach (map prod, add_on_products, {
  926.         string media = prod["media_url"]:"";
  927.         string pth = prod["product_dir"]:"/";
  928.         integer src = Pkg::SourceCreate (media, pth);
  929.         if (src != -1)
  930.         mode_config_sources = add (mode_config_sources, src);
  931.     });
  932.     }
  933.     return true;
  934. }
  935.  
  936. global void CleanModeConfigSources () {
  937.     foreach (integer src, mode_config_sources, {
  938.     Pkg::SourceDelete (src);
  939.     });
  940.     mode_config_sources = [];
  941. }
  942.  
  943.  
  944. } // module end
  945.