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 / include / runlevel / ui.ycp < prev    next >
Text File  |  2006-11-29  |  51KB  |  1,774 lines

  1. /**
  2.  * File:
  3.  *   ui.ycp
  4.  *
  5.  * Module:
  6.  *   System Services (Runlevel) (formerly known as Runlevel Editor)
  7.  *
  8.  * Summary:
  9.  *   Runlevel Editor user interface.
  10.  *
  11.  * Authors:
  12.  *   Martin Vidner <mvidner@suse.cz>
  13.  *   Petr Blahos <pblahos@suse.cz>
  14.  *   Martin Lazar <mlazar@suse.cz>
  15.  *
  16.  * $Id: ui.ycp 33299 2006-10-10 09:39:23Z locilka $
  17.  *
  18.  * Runlevel editor user interface functions.
  19.  */
  20.  
  21. {
  22.     textdomain "runlevel";
  23.  
  24.     import "Service";
  25.     import "RunlevelEd";
  26.     import "Wizard";
  27.     import "Label";
  28.     import "Popup";
  29.     import "FileUtils";
  30.  
  31.     define string StartedText (integer started);
  32.     define string BstatusText (boolean disabled, integer started);
  33.     define list mapkeys (map m);
  34.     define string formatLine (list<string> l, integer len);
  35.     define list startStopService (string service_name, string command);
  36.     define boolean LongContinueCancelHeadlinePopup (string headline, term richtext, integer hdim, integer vdim);
  37.     define void setServiceToDefault (string service_name);
  38.     define map<string, boolean> tomap_true (list<string> l);
  39.     define void updateServiceStatus (boolean use_func, string service_name);
  40.  
  41.     string current_service = "";
  42.     boolean do_abort_now = false;
  43.  
  44.     // We read service status when dialog with services is shown.
  45.     // We read status for services taken from list of services (service_list)
  46.     // and then update map services.
  47.     integer fetching_service_index = 0;
  48.     // when fetching_service_status becomes false, we stop fetching services
  49.     boolean fetching_service_status = true;
  50.  
  51.     // columns in the table where these items are located
  52.     // -1 means it is not there
  53.     integer c_dirty = -1;
  54.     integer c_bstatus = -1;
  55.     integer c_rstatus = -1;
  56.     integer c_runlevels = -1;
  57.     integer c_descr = -1;
  58.     // index into table column for each runlevel
  59.     map runlevel2tableindex = $[];
  60.  
  61.     // map of services listed in table
  62.     map <string, boolean> service_ids_listed_in_table = $[];
  63.  
  64.     /**
  65.      * Create term of checkboxes for runlevel selection.
  66.      * @return HBox full of checkboxes.
  67.      */
  68.     define term getRlCheckBoxes () ``{
  69.     term rls = `HBox (`opt (`hstretch));
  70.     foreach (string i, RunlevelEd::runlevels, ``{
  71.         if (size (rls) > 1)
  72.         rls = add (rls, `HStretch ());
  73.         rls = add (rls, `CheckBox (`id (i), `opt (`notify), "&" + i));
  74.     });
  75.     return rls;
  76.     }
  77.     /**
  78.      * Changes value of a runlevel checkbox.
  79.      * Prevents triggering userinput by disabling notify.
  80.      * @param service @ref service
  81.      * @param rl which runlevel
  82.      */
  83.     define void updateRlCheckBox (map service, string rl) ``{
  84.     list start = service["start"]:[];
  85.     boolean old_notify = (boolean) UI::QueryWidget (`id (rl), `Notify);
  86.     UI::ChangeWidget (`id (rl), `Notify, false);
  87.     UI::ChangeWidget (`id (rl), `Value, contains (start, rl));
  88.     UI::ChangeWidget (`id (rl), `Notify, old_notify);
  89.     }
  90.     /**
  91.      * Changes values of runlevel checkboxes.
  92.      * @param service @ref service
  93.      */
  94.     define void updateRlCheckBoxes (map service) ``{
  95.     foreach (string i, RunlevelEd::runlevels, ``{
  96.         updateRlCheckBox (service, i);
  97.     });
  98.     }
  99.     /**
  100.      * Update the long description text box
  101.      * @param service @ref service
  102.      */
  103.     define void updateDescription (map service) ``{
  104.     // For the box, use the long description.
  105.     // The short one as a fallback. #20853
  106.     string descr = service["description"]:"";
  107.     if (descr == "")
  108.     {
  109.         descr = service["shortdescription"]:"";
  110.     }
  111.     UI::ChangeWidget (`id (`description), `Value, descr);
  112.     }
  113.     /**
  114.      * Sets runlevel columns in the table.
  115.      * @param service_name which line
  116.      * @param service @ref service
  117.      * @param rls which columns to update, nil == all
  118.      */
  119.     define void updateRlColumns (string service_name, map service,
  120.                     list<string> rls) ``{
  121.     if (c_runlevels == -1)
  122.     {
  123.         return;
  124.     }
  125.  
  126.     list start = service["start"]:[];
  127.     if (rls == nil)
  128.     {
  129.         rls = RunlevelEd::runlevels;
  130.     }
  131.     foreach (string rl, rls, ``{
  132.         UI::ChangeWidget (`id (`table),
  133.                   `Item (service_name, runlevel2tableindex[rl]:-1),
  134.                   contains (start, rl) ? rl : " ");
  135.     });
  136.     }
  137.  
  138.     /**
  139.      * Returns whether the service is listed in the table of services
  140.      *
  141.      * @param string service_name
  142.      */
  143.     define boolean ServiceListedInTable (string service_name) {
  144.     return service_ids_listed_in_table[service_name]:false;
  145.     }
  146.  
  147.     /**
  148.      * Update run-time status column.
  149.      * @param service_name which line
  150.      * @param started status or -1 (unknown yet)
  151.      */
  152.     define void updateStatusColumn (string service_name, integer started) ``{
  153.     if (c_rstatus >= 0) {
  154.         // cannot change item which is not listed in the table
  155.         if (ServiceListedInTable(service_name))
  156.         UI::ChangeWidget(`id(`table), `Item(service_name, c_rstatus), StartedText(started));
  157.     }
  158.     if (c_bstatus >= 0) {
  159.         
  160.         // cannot change item which is not listed in the table
  161.         if (ServiceListedInTable(service_name)) {
  162.         boolean disabled = RunlevelEd::isDisabled(RunlevelEd::services[service_name]:$[]);
  163.         UI::ChangeWidget(`id(`table), `Item(service_name, c_bstatus), BstatusText(disabled, started));
  164.         }
  165.     }
  166.     }
  167.     /**
  168.      * Helper function for fetching service status in run-time.
  169.      * @param service_name which line
  170.      * @param service @ref service
  171.      * @param started status or -1 (unknown yet)
  172.      */
  173.     define void updateStatusInTable (string service_name, map service,
  174.                      integer started) ``{
  175.     // just translate the arguments. the callback is generic
  176.     // because of the future simple UI, bug #13789
  177.     updateStatusColumn (service_name, started);
  178.     }
  179.  
  180.     /**
  181.      * Changes values of runlevel checkboxes.
  182.      * Get the status if not known yet.
  183.      */
  184.     define void changeService1 (map service) ``{
  185.     if (service["started"]:-1 < 0)
  186.     {
  187.         integer started = Service::RunInitScriptWithTimeOut (current_service,
  188.         "status" + sformat(" 2>&1 1>%1/runlevel_out", SCR::Read(.target.tmpdir)));
  189.         service["started"] = started;
  190.         RunlevelEd::services[current_service] = service;
  191.         updateStatusColumn (current_service, started);
  192.     }
  193.     updateRlCheckBoxes (service);
  194.     updateDescription (service);
  195.     }
  196.  
  197.     /**
  198.      * Reads data from checkboxes and updates service
  199.      * and RunlevelEd::services maps.
  200.      * @param service_name which service
  201.      * @param service @ref service
  202.      * @return @ref service
  203.      */
  204.     define map queryRlCheckBoxes (string service_name, map service) ``{
  205.     list start_in = [];
  206.     foreach (string i, RunlevelEd::runlevels, ``{
  207.         if ((boolean) UI::QueryWidget (`id (i), `Value))
  208.         {
  209.         start_in[size(start_in)] = i;
  210.         }
  211.     });
  212.     if (service["start"]:[] != start_in)
  213.     {
  214.         service = union (service, $[
  215.                  "start": start_in,
  216.                  "dirty": true
  217.                  ]);
  218.         RunlevelEd::services[service_name] = service;
  219.     }
  220.     return service;
  221.     }
  222.  
  223.     // mapping numbers to descriptions
  224.     /**
  225.      * Get help text for rcscript start|stop command exit value.
  226.      * @param exit exit value
  227.      * @return string help text
  228.      */
  229.     define string getActionReturnHelp (integer exit) ``{
  230.     map <integer, string> descr =
  231.         $[
  232.         // Init script non-status-command return codes
  233.         // http://www.linuxbase.org/spec/gLSB/gLSB/iniscrptact.html
  234.         // status code.
  235.         // Changed "Foo bar." to "foo bar", #25082
  236.         0: _("success"),
  237.         /* 1: handled as default below */
  238.         // status code.
  239.         2: _("invalid or excess arguments"),
  240.         // status code.
  241.         3: _("unimplemented feature"),
  242.         // status code.
  243.         4: _("user had insufficient privileges"),
  244.         // status code.
  245.         5: _("program is not installed"),
  246.         // status code.
  247.         6: _("program is not configured"),
  248.         // status code.
  249.         7: _("program is not running"),
  250.         ];
  251.     // status code.
  252.     return descr[exit]:_("unspecified error");
  253.     }
  254.     /**
  255.      * Get help text for rcscript status return value
  256.      * according to LSB.
  257.      * @param exit exit value
  258.      * @return string help text
  259.      */
  260.     define string getStatusReturnHelp (integer exit) ``{
  261.     map <integer, string> descr =
  262.         $[
  263.         // Init script "status" command return codes
  264.         // http://www.linuxbase.org/spec/gLSB/gLSB/iniscrptact.html
  265.         // status code.
  266.         // Changed "Foo bar." to "foo bar", #25082
  267.         0: _("program is running"),
  268.         // status code.
  269.         1: _("program is dead and /var/run pid file exists"),
  270.         // status code.
  271.         2: _("program is dead and /var/lock lock file exists"),
  272.         // status code.
  273.         3: _("program is stopped"),
  274.         // status code.
  275.         4: _("program or service status is unknown"),
  276.         ];
  277.     // status code.
  278.     return descr[exit]:_("unspecified error");
  279.     }
  280.  
  281.     /**
  282.      * @param    rll    a list of runlevels or nil, meaning "all"
  283.      * @return        "in [these] runlevels" (translated)
  284.      */
  285.     define string getInRunlevels (list<string> rll) ``{
  286.     if (rll == nil)
  287.     {
  288.         // translators: substituted into a message like
  289.         // "To enable/disable foo IN ALL RUNLEVELS, this must be done..."
  290.         // (do not include the trailing comma here)
  291.         return _("in all runlevels");
  292.     }
  293.     else
  294.     {
  295.         string s = mergestring (rll, ", ");
  296.         // translators: substituted into a message like
  297.         // "To enable/disable foo IN RUNLEVELS 3, 5, this must be done..."
  298.         // (do not include the trailing comma here)
  299.         return size(rll)>1 ? sformat(_("in runlevels %1"), s) : sformat(_("in runlevel %1"), s);
  300.     }
  301.     }
  302.  
  303.     /**
  304.      * @param started status or -1 (unknown yet)
  305.      * @return "Yes", "No" or "???"
  306.      */
  307.     define string StartedText (integer started) ``{
  308.     return (0 == started ?
  309.         // is the service started?
  310.         _("Yes") :
  311.         (started > 0 ?
  312.          // is the service started?
  313.          _("No") :
  314.          // is the service started?
  315.          // ???: we do not know yet what is the service state
  316.          _("???")));
  317.     }
  318.     
  319.     /**
  320.      * @param disabled status
  321.      * @param started status or -1 (unknown yet)
  322.      * @return "Yes", "Yes*", "No", "No*" or "???"
  323.      */
  324.     define string BstatusText (boolean disabled, integer started) ``{
  325.     // TRANSLATORS: Unknown service status, presented in table
  326.     string state = _("???");
  327.     if (disabled != nil) {
  328.         // TRANSLATORS: Unknown service status presented in table
  329.         state = (started<0) ? _("???") : (!disabled ?
  330.         // TRANSLATORS: Service status presented in table, Enabled: Yes
  331.         _("Yes")
  332.         :
  333.         // TRANSLATORS: Service status presented in table, Enabled: No
  334.         _("No")
  335.         );
  336.         if (started>=0 && (started>0 != disabled)) {
  337.         state = state + "*";
  338.         }
  339.     }
  340.     return state;
  341.     }
  342.  
  343.  
  344.     //start unsorted
  345.     /**
  346.      * Ask if really abort. Uses boolean changed_settings. Sets boolean do_abort_now.
  347.      * @return boolean true if user really wants to abort
  348.      */
  349.     define boolean reallyAbort ()``{
  350.     if (do_abort_now || !RunlevelEd::isDirty ())
  351.         {
  352.         do_abort_now = true;
  353.         return true;
  354.         }
  355.     do_abort_now = Popup::ReallyAbort (true);
  356.     return do_abort_now;
  357.     }
  358.  
  359.  
  360.     /**
  361.      * Create table items from services.
  362.      * For simple mode, also filter out critical services: boot ones.
  363.      * For Expert mode:
  364.      * Mixin: started, start, (short)description.
  365.      * @param mix which items to mix in:<pre>
  366.      *`simple:    id=name, name, bstatus,            (short)description
  367.      *`complex:    id=name, name, rstatus, runlevels, (short)description
  368.      *`auto:    id=name, name, dirty,   runlevels, ?(short)description
  369.      *</pre>
  370.      * @return list List of items for table.
  371.      */
  372.     define list servicesToTable (symbol mix) ``{
  373.     boolean m_dirty = mix == `auto;
  374.     boolean m_bstatus = mix == `simple;
  375.     boolean m_rstatus = mix == `complex;
  376.     boolean m_runlevels = mix != `simple;
  377.     boolean m_descr = true;
  378.  
  379.     // assume it is not there until placed in
  380.     c_dirty = -1;
  381.     c_bstatus = -1;
  382.     c_rstatus = -1;
  383.     c_runlevels = -1;
  384.     c_descr = -1;
  385.     runlevel2tableindex = $[];
  386.  
  387.     // filter out services that are too important to be messed with
  388.     // in the simple mode
  389.     map<string,map> services = RunlevelEd::services;
  390.     if (mix == `simple)
  391.     {
  392.         services = (map<string, map>)filter (string s_name, map s, services, ``(
  393.                    ! contains (s["defstart"]:[], "B")
  394.                    ));
  395.     }
  396.  
  397.     list items = [];
  398.     boolean first = true;
  399.     foreach (string k, map v, services, ``{
  400.         if (first)
  401.         {
  402.         first = false;
  403.         // preserve current service when switching modes
  404.         if (!haskey (services, current_service))
  405.         {
  406.             current_service = k;
  407.         }
  408.         }
  409.         // id=name, name
  410.         term item = `item (`id (k), k);
  411.         // column where a item is added
  412.         integer col = 1;
  413.         if (m_dirty)
  414.         {
  415.         // dirty
  416.         service_ids_listed_in_table[k] = true;
  417.         c_dirty = col;
  418.         item = add (item, v["dirty"]:false ? UI::Glyph(`CheckMark) : " ");
  419.         col = col + 1;
  420.         }
  421.         if (m_bstatus)
  422.         {
  423.         // boot status
  424.         service_ids_listed_in_table[k] = true;
  425.         boolean disabled = RunlevelEd::isDisabled(v);
  426.         integer started = v["started"]:-1;
  427.         c_bstatus = col;
  428.         item = add (item, BstatusText(disabled, started));
  429.         col = col + 1;
  430.         }
  431.         if (m_rstatus)
  432.         {
  433.         // runtime status
  434.         service_ids_listed_in_table[k] = true;
  435.         integer started = v["started"]:-1;
  436.         c_rstatus = col;
  437.         item = add (item, StartedText (started));
  438.         col = col + 1;
  439.         }
  440.         if (m_runlevels)
  441.         {
  442.         // runlevels
  443.         service_ids_listed_in_table[k] = true;
  444.         list rl = v["start"]:[];
  445.         c_runlevels = col;
  446.         foreach (string i, RunlevelEd::runlevels, ``{
  447.             runlevel2tableindex[i] = col;
  448.             item = add (item, (contains (rl, i)? i : " "));
  449.             col = col + 1;
  450.         });
  451.         }
  452.         if (m_descr)
  453.         {
  454.         // (short)description
  455.         // For the table, use the short description.
  456.         // The long one as a fallback. #20853
  457.         service_ids_listed_in_table[k] = true;
  458.         string descr = v["shortdescription"]:"";
  459.         if (descr == "")
  460.         {
  461.             descr = v["description"]:"";
  462.         }
  463.         c_descr = col;
  464.         item = add (item, descr);
  465.         col = col + 1;
  466.         }
  467.         // next
  468.         items[size(items)] = item;
  469.     });
  470.     return items;
  471.     }
  472.  
  473.     map <string, boolean> fetched_service_status = $[];
  474.  
  475.     /**
  476.      * For each service, determines its status and calls a supplied function.
  477.      * @param func function to call
  478.      * @see updateStatusInTable
  479.      */
  480.     define void serviceStatusIterator (boolean use_func) ``{
  481.     if (!fetching_service_status)
  482.     {
  483.         return;
  484.     }
  485.     if (fetching_service_index >= size (RunlevelEd::service_list))
  486.     {
  487.         fetching_service_status = false;
  488.         return;
  489.     }
  490.     string service_name = RunlevelEd::service_list[fetching_service_index]:"";
  491.     fetching_service_index = fetching_service_index + 1;
  492.  
  493.     // every switch between `complex and `simple the fetching_service_index is changed to zero
  494.     // but only services which were not checked before are checked now
  495.     if (fetched_service_status[service_name]:false == false) {
  496.         if (ServiceListedInTable(service_name)) {
  497.             updateServiceStatus(use_func, service_name);
  498.         fetched_service_status[service_name] = true;
  499.         }
  500.     }
  501.     }
  502.  
  503.     /**
  504.      * For specified service, determines its status and calls a supplied function.
  505.      * @param func function to call
  506.      * @see updateStatusInTable
  507.      */
  508.     define void updateServiceStatus (boolean use_func, string service_name) ``{
  509.     if (RunlevelEd::services[service_name, "started"]:-1 < 0) {
  510.             integer started = Service::RunInitScriptWithTimeOut (service_name,
  511.         "status" + sformat(" 2>&1 1>%1/runlevel_out", SCR::Read(.target.tmpdir)));
  512.  
  513.         RunlevelEd::services[service_name, "started"] = started;
  514.  
  515.             if (use_func)
  516.         updateStatusInTable(service_name, RunlevelEd::services[service_name]:$[], started);
  517.     }
  518.     }
  519.  
  520.     /**
  521.      * help text for progress
  522.      * @return help text
  523.      */
  524.     define string getHelpProgress () ``{
  525.     return
  526.         // help text
  527.         _("<P><BIG><B>System Service (Runlevel) Initialization</B></BIG><BR>
  528. Please wait...</P>
  529. ")
  530.         +
  531.         // warning
  532.         _("<p><b>Note:</b> The system services (runlevel editor) is an expert tool. Only change settings if
  533.  you know what you are doing.  Otherwise your system might not function properly afterwards.</p>
  534. ")
  535.         ;
  536.     }
  537.  
  538.     /**
  539.      * Enable or disable a service in some runlevels.
  540.      * Set the variables and update the ui (rl columns).
  541.      * @param service_name    a service
  542.      * @param rls    which runlevels, nil == disable in all
  543.      * @param enable    enabling or disabling?
  544.      */
  545.     define void SetService (string service_name,
  546.                    list<string> rls, boolean enable) ``{
  547.     map service = RunlevelEd::services[service_name]:$[];
  548.     list<string> start_in = nil;
  549.     if (rls == nil)
  550.     {
  551.         start_in = [];
  552.     }
  553.     else
  554.     {
  555.         map<string, boolean> start = tomap_true (service["start"]:[]);
  556.         foreach (string rl, rls, ``{
  557.         start = (map<string, boolean>) add (start, rl, enable);
  558.         });
  559.         start = (map<string, boolean>) filter (string k, boolean v, start, ``( v == true));
  560.         start_in = (list<string>) mapkeys (start);
  561.     }
  562.  
  563.     if (service["start"]:[] != start_in)
  564.     {
  565.         service = union (service, $[
  566.                  "start": start_in,
  567.                  "dirty": true
  568.                  ]);
  569.         RunlevelEd::services[service_name] = service;
  570.  
  571.         updateRlColumns (service_name, service, rls);
  572.         updateStatusColumn (service_name, service["started"]:-1);
  573.     }
  574.     }
  575.  
  576.     /**
  577.      * Check that all the services exist (in RunlevelEd::services).
  578.      * If not, popup a list of the missing ones and ask whether
  579.      * continue or not. Filter out the missing ones.
  580.      * @param services a service list
  581.      * @return [continue?, filtered list]
  582.      */
  583.     define list CheckMissingServices (list<string> services) ``{
  584.     list<string> missing = [];
  585.     boolean ok = true;
  586.     services = filter (string s, services, ``{
  587.         if (haskey (RunlevelEd::services, s))
  588.         {
  589.         return true;
  590.         }
  591.         else
  592.         {
  593.         missing[size(missing)] = s;
  594.         return false;
  595.         }
  596.     });
  597.     if (size (missing) > 0)
  598.     {
  599.         // missing services only occur when enabling
  600.         ok = Popup::ContinueCancel (
  601.         sformat (
  602.             // continue-cancel popup when enabling a service
  603.             // %1 is a list of unsatisfied dependencies
  604.             _("These required services are missing:\n%1."),
  605.             formatLine (missing, 40)));
  606.     }
  607.     return [ok, services];
  608.     }
  609.  
  610.  
  611.     /**
  612.      * Generic function to handle enabling, disabling, starting and
  613.      * stoping services and their dependencies, in various runlevels.
  614.      * Piece of cake ;-) <br>
  615.  
  616.      * Either of init_time or run_time can be specified (for complex
  617.      * mode) or both (for simple mode).
  618.  
  619.      * rls: ignored for -init +run
  620.  
  621.      * What it does: gets dependent services (in the correct order),
  622.      * filters ones that are already in the desired state, if there
  623.      * are dependencies left, pop up a confirmation dialog, check for
  624.      * missing dependencies, perform the action (run-time, then init-time)
  625.      * for the deps and the
  626.      * service (in this order), displaying output after each error and
  627.      * at the end.
  628.      *
  629.  
  630.      * @param service_name    name of service
  631.      * @param rls        in which run levels, nil == all
  632.      * @param enable        on/off
  633.      * @param init_time        do enable/disable
  634.      * @param run_time        do start/stop
  635.      * @return success (may have been canceled because of dependencies)
  636.      */
  637.     define boolean ModifyServiceDep (string service_name,
  638.                         list<string> rls,
  639.                         boolean enable,
  640.                         boolean init_time,
  641.                         boolean run_time) ``{
  642.     y2debug (1, "Modify: %1 %2 %3 %4 %5",
  643.          service_name,
  644.          rls,
  645.          enable,
  646.          init_time? "+init": "-init",
  647.          run_time? "+run": "-run");
  648.     boolean one = (rls != nil) ? (size (rls) == 1) : false;
  649.     string command = enable? "start": "stop";
  650.  
  651.     // get dependent services
  652.     list<string> dep_s = RunlevelEd::ServiceDependencies (service_name, enable);
  653.     y2debug ("DEP: %1", dep_s);
  654.  
  655.         // ensure we have already determined the service status (#36171)
  656.         foreach(string service_name, dep_s, {
  657.         updateServiceStatus(false, service_name);
  658.     });
  659.  
  660.     // filter ones that are ok already
  661.     dep_s = RunlevelEd::FilterAlreadyDoneServices (dep_s, rls, enable,
  662.                                init_time, run_time);
  663.     y2debug ("DEP filtered: %1", dep_s);
  664.  
  665.     boolean doit = size (dep_s) == 0;
  666.     // if there are dependencies left, pop up a confirmation dialog
  667.     if (!doit)
  668.     {
  669.         string key =
  670.         (run_time? "+run": "-run") + "," +
  671.         (init_time? "+init": "-init") + "," +
  672.         (enable? "on": "off");
  673.         map texts = $[
  674.         //"-run,-init,..." does not make sense
  675.  
  676.         // *disable*
  677.         // continue-cancel popup
  678.         // translators: %2 is "in runlevel(s) 3, 5"
  679.         // or "in all runlevels"
  680.         "-run,+init,off" : _("To disable service %1 %2,
  681. these services must be additionally disabled,
  682. because they depend on it:
  683. %3."),
  684.         // *enable*
  685.         // continue-cancel popup
  686.         // translators: %2 is "in runlevel(s) 3, 5"
  687.         // or "in all runlevels"
  688.         "-run,+init,on" : _("To enable service %1 %2,
  689. these services must be additionally enabled,
  690. because it depends on them:
  691. %3."),
  692.         // *stop*
  693.         // continue-cancel popup
  694.         "+run,-init,off" : _("To stop service %1,
  695. these services must be additionally stopped,
  696. because they depend on it:
  697. %2."),
  698.         // *start*
  699.         // continue-cancel popup
  700.         "+run,-init,on" : _("To start service %1,
  701. these services must be additionally started,
  702. because it depends on them:
  703. %2."),
  704.         // *stop and disable*
  705.         // continue-cancel popup
  706.         // translators: %2 is "in runlevel(s) 3, 5"
  707.         // or "in all runlevels"
  708.         "+run,+init,off" :_("To stop service %1 and disable it %2,
  709. these services must be additionally stopped
  710. and disabled, because they depend on it:
  711. %3."),
  712.         // *start and enable*
  713.         // continue-cancel popup
  714.         // translators: %2 is "in runlevel(s) 3, 5"
  715.         // or "in all runlevels"
  716.         "+run,+init,on" : _("To start service %1 and enable it %2,
  717. these services must be additionally started
  718. and enabled, because it depends on them:
  719. %3."),
  720.         ];
  721.         string dep_formatted = formatLine (dep_s, 40);
  722.         doit = Popup::ContinueCancel (
  723.         sformat (
  724.             texts[key]:"?",
  725.             service_name,
  726.             // non-init-time does not need the runlevels
  727.             // so we omit it not to confuser the translators
  728.             init_time? getInRunlevels (rls): dep_formatted,
  729.             dep_formatted
  730.             )
  731.         );
  732.     }
  733.  
  734.     // check for missing services
  735.     if (doit)
  736.     {
  737.         list r = CheckMissingServices (dep_s);
  738.         doit = r[0]:false;
  739.         dep_s = r[1]:[];
  740.     }
  741.  
  742.  
  743.     string rich_message = "";
  744.     if (doit)
  745.     {
  746.         // iterate dep_s, not including service_name
  747.         // because we will ask "continue?" on error
  748.         // Foreach with a break: find the failing one
  749.         find (string s, dep_s, ``{
  750.         if (run_time)
  751.         {
  752.             list ret = startStopService (s, command);
  753.  
  754.             rich_message = rich_message + ret[1]:"";
  755.             integer exit = ret[0]:-1;
  756.             if (exit != 0)
  757.             {
  758.             doit = LongContinueCancelHeadlinePopup (
  759.                 // popup heading
  760.                 _("An error has occurred."), `RichText (rich_message),
  761.                 70, 10);
  762.             // don't show what we've already seen
  763.             rich_message = "";
  764.             if (!doit)
  765.             {
  766.                 return true; // break(foreach)
  767.             }
  768.             }
  769.         }
  770.         if (init_time)
  771.         {
  772.             // set the variables and update the ui
  773.             SetService (s, rls, enable);
  774.         }
  775.         return false;
  776.         });
  777.     }
  778.     if (doit)
  779.     {
  780.         // only for service_name
  781.         if (run_time)
  782.         {
  783.         list ret = startStopService (service_name, command);
  784.  
  785.         rich_message = rich_message + ret[1]:"";
  786.         Popup::LongText ("", `RichText(rich_message), 70, 5);
  787.  
  788.                 // don't enable the service if it can't be started, #36176
  789.                 if (ret[0]:-1 != 0) return false;
  790.         }
  791.  
  792.             if (init_time)
  793.         {
  794.         // set the variables and update the ui
  795.         SetService (service_name, rls, enable);
  796.         }
  797.     }
  798.     return doit;
  799.     }
  800.  
  801.  
  802.     /**
  803.      * Turns a service on or off in the simple mode, ie. resolving
  804.      * dependencies and for each service doing start,enable or
  805.      * stop,disable.
  806.      * @param service_name    name of service
  807.      * @param rls        in which run levels, nil == all
  808.      * @return success (may have been canceled because of dependencies)
  809.      */
  810.     define boolean SimpleSetServiceDep (string service_name,
  811.                            boolean enable) ``{
  812.     list<string> rls = enable?
  813.         RunlevelEd::services[current_service, "defstart"]:[] :
  814.         nil;
  815.     return ModifyServiceDep (service_name, rls, enable, true, true);
  816.     }
  817.  
  818.  
  819.     /**
  820.      * Used for enabling/disabling a service and services depending on
  821.      * it in a runlevel or a set of runlevels.
  822.      * @param service_name    name of service
  823.      * @param rls        in which run levels, nil == all
  824.      * @param enable        enable/disable
  825.      * @return success (may have been canceled because of dependencies)
  826.      */
  827.     define boolean EnableDisableServiceDep (string service_name,
  828.                            list<string> rls,
  829.                            boolean enable) ``{
  830.     return ModifyServiceDep (service_name, rls, enable, true, false);
  831.     }
  832.  
  833.     /**
  834.      * Used for starting/stopping a service and services depending on it.
  835.      * Displays result popups.
  836.      * @param service_name    name of service
  837.      * @param enable        start/stop
  838.      * @return success (may have been canceled because of dependencies)
  839.      */
  840.     define boolean StartStopServiceDep (string service_name,
  841.                            boolean enable) ``{
  842.     return ModifyServiceDep (service_name, [], enable, false, true);
  843.     }
  844.  
  845.  
  846.     /**
  847.      * Starts/stops/checks status of a service
  848.      * @param service_name service to start/stop
  849.      * @param command "start" or "stop" or "status"
  850.      * @return [integer exit_status, string rich_message]
  851.      */
  852.     define list startStopService (string service_name,
  853.                      string command) ``{
  854.     UI::OpenDialog (`Label (service_name + " " + command));
  855.     y2milestone("%1 -> %2", service_name, command);
  856.     string log_filename = sformat("%1/runlevel_out", (string) SCR::Read(.target.tmpdir));
  857.     integer cmd_run = Service::RunInitScriptWithTimeOut (
  858.         service_name,
  859.         command + sformat(" 2>&1 1>%1", log_filename)
  860.     );
  861.     map out = $[ "exit" : cmd_run ];
  862.     if (FileUtils::Exists(log_filename)) {
  863.         out["stdout"] = (string) SCR::Read(.target.string, log_filename);
  864.     }
  865.     
  866.     UI::CloseDialog ();
  867.  
  868.     integer exit = out["exit"]:-1;
  869.     string rich_message = sformat (
  870.         // %1 service, %2 command,
  871.         // %3 exit status, %4 status description, %5 stdout
  872.         // added a colon
  873.         _("<p><b>/etc/init.d/%1 %2</b> returned %3 (%4):<br>%5</p>"),
  874.         service_name, command, exit,
  875.         (command == "status" ?
  876.          getStatusReturnHelp (exit) :
  877.          getActionReturnHelp (exit)),
  878.         sformat ("<pre>%1</pre>", out["stdout"]:"")
  879.         );
  880.     // succesful stop => status: program not running
  881.     integer started = (exit == 0 && command == "stop")? 3: exit;
  882.     // normally "started" has the exit code of "status",
  883.     // and we may be adding output of a different command
  884.     // but it is only tested against zero anyway
  885.     map service = RunlevelEd::services[service_name]:$[];
  886.     service["started"] = started;
  887.     RunlevelEd::services[service_name] = service;
  888.     // this won't work in simple mode
  889.     // make a map "item"->column_number, depending on mode
  890.     // then the functions can consult it and do nothing if not present
  891.     updateStatusColumn (service_name, service["started"]:-1);
  892.     return [exit, rich_message];
  893.     }
  894.  
  895.     /**
  896.      * Prints list items into a string, separating them by commas
  897.      * and when line exceeds len characters, it does line break (\n).
  898.      * It adds 5 spaces before each line.
  899.      * Do not expect reasonable results if you set len < 0.
  900.      * @param l list of strings
  901.      * @param len minimal length of line
  902.      * @return string formated string
  903.      */
  904.     define string formatLine (list<string> l, integer len) ``{
  905.     string s = "";
  906.     string line = "     ";
  907.     string add_sep = "";
  908.     string line_sep = "";
  909.     foreach (string i, l, ``{
  910.         if (size (line) > len)
  911.         {
  912.             s = s + line_sep + line;
  913.             line_sep = ",\n";
  914.             line = "     ";
  915.         }
  916.         else
  917.         {
  918.             line = line + add_sep;
  919.         }
  920.         line = line + i;
  921.         add_sep = ", ";
  922.     });
  923.     if (size (line) > 0)
  924.         {
  925.         s = s + line_sep + line;
  926.         }
  927.     return s;
  928.     }
  929.  
  930.     /**
  931.      * Checks what services should run in this runlevel and do not run
  932.      * or what services run but should not run.
  933.      * @return string overview text
  934.      */
  935.     define string overviewText () ``{
  936.     list<string> should_not_run = [];
  937.     list<string> should_run = [];
  938.     foreach (string k, map v, RunlevelEd::services, ``{
  939.         if (RunlevelEd::StartContainsImplicitly  (v["start"]:[],
  940.                               RunlevelEd::current))
  941.         {   // it should run
  942.             if (0 != v["started"]:-1)
  943.             {
  944.                 should_run[size(should_run)] = k;
  945.             }
  946.         }
  947.         else
  948.         {
  949.             if (0 == v["started"]:-1)
  950.             {
  951.                 should_not_run[size(should_not_run)] = k;
  952.             }
  953.         }
  954.     });
  955.     string s = "";
  956.     if (size (should_run) + size (should_not_run) > 0)
  957.         {
  958.         // message label
  959.         s = s + "\n\n"/* + _("Overview") + "\n\n"*/;
  960.         if (size (should_not_run) > 0)
  961.             {
  962.             // list of services will follow
  963.             s = s + _("Following services run in current\nrunlevel although they should not:");
  964.             s = s + "\n" + formatLine (should_not_run, 35) + "\n\n";
  965.             }
  966.  
  967.         if (size (should_run) > 0)
  968.             {
  969.             // list of services will follow
  970.             s = s + _("Following services do not run in current\nrunlevel although they should:");
  971.             s = s + "\n" + formatLine (should_run, 35) + "\n\n";
  972.             }
  973.         }
  974.     return s;
  975.     }
  976.  
  977.     /**
  978.      * Radio buttons (faking tabs) for switching modes
  979.      * @param mode `simple or `complex, which one are we in
  980.      * @return RadioButtonGroup term
  981.      */
  982.     define term ModeTabs (symbol mode) ``{
  983.     term rbg =
  984.         // fake tabs using radio buttons
  985.         `RadioButtonGroup (
  986.         `id (`rbg),
  987.         `HBox (
  988.             `RadioButton (`id (`simple), `opt (`notify),
  989.                   // radio button label
  990.                   _("&Simple Mode"), mode == `simple),
  991.             `HSpacing (3),
  992.             `RadioButton (`id (`complex), `opt (`notify, `key_F7),
  993.                   // radio button label
  994.                   _("&Expert Mode"), mode == `complex)
  995.             )
  996.         );
  997.     return `Left (rbg);
  998.     }
  999.  
  1000.  
  1001.     /**
  1002.      * help text services dialog
  1003.      * @return help text
  1004.      */
  1005.     define string getHelpComplex () ``{
  1006.     // help text
  1007.     return
  1008.         // help text
  1009.         _("<p>Assign system services to runlevels by selecting the list entry of the respective service then
  1010. checking or unchecking the <b>check boxes B-S</b> for the runlevel.</p>
  1011. ")
  1012.         +
  1013.         // help text
  1014.         _("<p><b>Start/Stop/Refresh:</b> Use this to start or stop services individually.</p>")
  1015.         +
  1016.         // help text
  1017.         _("<P><B>Set and Reset:</B>
  1018. Select runlevels in which to run the currently selected service.<ul>
  1019. <li><b>Enable the service:</b> Activates the service in the standard runlevels.</li>
  1020. <li><b>Disable the service:</b> Deactivates service.</li>
  1021. <li><b>Enable all services:</b> Activates all services in their standard runlevels.</li>
  1022. </ul></p>
  1023. ")
  1024.         +
  1025.         // The change does not occur immediately. After a reboot the system boots into the specified runlevel.
  1026.         _("<p>Changes to the <b>default runlevel</b> will take effect next time you boot your computer.</p>")
  1027.         ;
  1028.     }
  1029.  
  1030.     /**
  1031.      * Main dialog for changing services.
  1032.      * @return symbol for wizard sequencer
  1033.      */
  1034.     define symbol ComplexDialog () ``{
  1035.     Wizard::SetScreenShotName ("runlevel-2-services");
  1036.  
  1037.     // currently selected service we are working with
  1038.     map service = $[];
  1039.  
  1040.     term header = `header (
  1041.         // table header
  1042.         _("Service"),
  1043.         // table header. is a service running?
  1044.         _("Running"));
  1045.     foreach (string i, RunlevelEd::runlevels, ``{
  1046. //        header = add (header, `Center (" " + i + " "));
  1047.         header = add (header, `Center (i));
  1048.     });
  1049.     // headers in table
  1050.     header = add (header, _("Description"));
  1051.  
  1052.     list args = WFM::Args ();
  1053.     // should we show debugging buttons?
  1054.     boolean show_debug = args[0]:"" == "debug";
  1055.     // should we show the Restore to default button?
  1056.     boolean show_restore = true;
  1057.  
  1058.     term contents = `VBox (
  1059.         `VSpacing (0.4),
  1060.         ModeTabs (`complex),
  1061.         /*
  1062.         `HBox (
  1063.         // preserve 2 spaces at the end.
  1064.         `Label (_("Current runlevel:  ")),
  1065.         `Label (`opt (`outputField, `hstretch), getRunlevelDescr (RunlevelEd::current))
  1066.         ),
  1067.         */
  1068.         // combo box label
  1069.         `ComboBox (`id (`default_rl), `opt (`hstretch), _("&Set default runlevel after booting to:"), RunlevelEd::getDefaultPicker (`complex)),
  1070.  
  1071.         `VSpacing (0.4),
  1072.         `Table (`id (`table), `opt (`notify, `immediate),
  1073.             header,
  1074.             servicesToTable (`complex)
  1075.         ),
  1076.         `VSquash (
  1077.         `HBox (
  1078.             `VSpacing (4.3), // 3+borders in qt, 3 in curses
  1079.             `RichText (`id (`description), `opt (`shrinkable, `vstretch), "")
  1080.             )
  1081.         ),
  1082.         `VBox (
  1083.         // label above checkboxes
  1084.         `Label (`id (`service_label), `opt (`hstretch), _("Service will be started in following runlevels:")),
  1085.         getRlCheckBoxes ()
  1086.         ),
  1087.         `HBox (
  1088.         // menubutton label
  1089.         `MenuButton (_("S&tart/Stop/Refresh"), [
  1090.                  // menu item
  1091.             `item (`id (`start), _("&Start now ...")),
  1092.                  // menu item
  1093.             `item (`id (`stop), _("S&top now ...")),
  1094.                  // menu item
  1095.             `item (`id (`status), _("&Refresh status ...")),
  1096.             ]),
  1097.         `HStretch (),
  1098.         `ReplacePoint (
  1099.             `id (`menubutton),
  1100.             // menubutton label
  1101.             `MenuButton (_("Set/&Reset"), [
  1102.                  // menu item
  1103.             `item (`id (`to_enable), /*`opt (`key_F3),*/ _("&Enable the service")),
  1104.                  // menu item
  1105.             `item (`id (`to_disable), /*`opt (`key_F5),*/ _("&Disable the service")),
  1106.             ]
  1107.             + (!show_restore ? [] :
  1108.             [
  1109.                 //TODO
  1110.             ])
  1111.             +
  1112.             [
  1113.                  // menu item
  1114.             `item (`id (`to_all_enable), _("Enable &all services")),
  1115.             ]
  1116.             + (!show_debug ? [] :
  1117.             [
  1118.                 // menu item
  1119.             `item (`id (`depviz), _("Save Dependency &Graph")),
  1120.             ])
  1121.             )
  1122.             )
  1123.         )
  1124.         );
  1125.     // dialog caption.
  1126.     Wizard::SetContents (_("System Services (Runlevel): Details"), contents, getHelpComplex (), true, true);
  1127.     Wizard::DisableBackButton ();
  1128.  
  1129.     UI::ChangeWidget (`id (`table), `CurrentItem, current_service);
  1130.     service = RunlevelEd::services[current_service]:$[];
  1131.     changeService1 (service);
  1132.  
  1133.     any ret = nil;
  1134.  
  1135.     // fetch service which were not checked before
  1136.     fetching_service_status = true;
  1137.     fetching_service_index  = 0;
  1138.  
  1139.     while (`next != ret && `back != ret && `abort != ret && `simple != ret)
  1140.         {
  1141.         if (ret != nil) y2milestone("RET: %1", ret);
  1142.         // Kludge, because a `Table still does not have a shortcut.
  1143.         // #16116
  1144.         UI::SetFocus (`id (`table));
  1145.  
  1146.         if (fetching_service_status)
  1147.         {
  1148.             ret = UI::PollInput ();
  1149.             UI::NormalCursor ();
  1150.             if (nil == ret)
  1151.             {
  1152.             serviceStatusIterator (true);
  1153.             continue;
  1154.             }
  1155.             UI::BusyCursor ();
  1156.         }
  1157.         else
  1158.         {
  1159.             ret = UI::UserInput ();
  1160.         }
  1161.         if (ret == `cancel)
  1162.         {
  1163.             ret = `abort;
  1164.         }
  1165.  
  1166.         current_service = (string) UI::QueryWidget (`id (`table), `CurrentItem);
  1167.  
  1168.         if (`abort == ret)
  1169.         {
  1170.             if (!reallyAbort ())
  1171.             {
  1172.             ret = nil;
  1173.             continue;
  1174.             }
  1175.         }
  1176.         else if (`next == ret)
  1177.         {
  1178.             // TODO: check dependencies of all services? (on demand?)
  1179.             // string nfs_adj = RunlevelEd::CheckPortmap ();
  1180.             // ...
  1181.  
  1182.             if (RunlevelEd::isDirty ())
  1183.             {
  1184.             // yes-no popup
  1185.             if (!Popup::YesNo (_("Now the changes to runlevels \nwill be saved.")))
  1186.             {
  1187.                 ret = nil;
  1188.                 continue;
  1189.             }
  1190.             }
  1191.             RunlevelEd::default_runlevel = (string) UI::QueryWidget (`id (`default_rl), `Value);
  1192.             break;
  1193.         }
  1194.         else if (`table == ret)
  1195.         {
  1196.             service = RunlevelEd::services[current_service]:$[];
  1197.             changeService1 (service);
  1198.         }
  1199.         else if (nil != ret && is (ret, string))
  1200.         {
  1201.             // checkbox pressed
  1202.             // - enable/disable current_service in one runlevel
  1203.             boolean enable = (boolean) UI::QueryWidget (`id (ret), `Value);
  1204.             list<string> rls = (list<string>) [ret];
  1205.             if (!EnableDisableServiceDep (current_service, (list<string>) [ret], enable))
  1206.             {
  1207.             // restore the check box
  1208.             updateRlCheckBox (service, (string) ret);
  1209.             }
  1210.         }
  1211.         else if (`depviz == ret)
  1212.         {
  1213.             string filename = UI::AskForSaveFileName(".", "*", "");
  1214.             SCR::Write (.target.string, filename,
  1215.                 RunlevelEd::DotRequires ());
  1216.         }
  1217.         else if (`to_enable == ret)
  1218.         {
  1219.             list<string> default_runlevel = RunlevelEd::services[current_service, "defstart"]:[];
  1220.             EnableDisableServiceDep (current_service, default_runlevel, true);
  1221.             service = RunlevelEd::services[current_service]:$[];
  1222.             changeService1 (service);
  1223.         }
  1224.         else if (`to_disable == ret)
  1225.         {
  1226.             EnableDisableServiceDep (current_service, nil, false);
  1227.             service = RunlevelEd::services[current_service]:$[];
  1228.             changeService1 (service);
  1229.         }
  1230.         else if (`to_all_enable == ret)
  1231.         {
  1232.             // yes-no popup
  1233.             if (Popup::YesNo (_("Really enable all services?")))
  1234.             {
  1235.             foreach (string k, map v, RunlevelEd::services, ``{
  1236.                 setServiceToDefault (k);
  1237.             });
  1238.             UI::ChangeWidget (`id (`table), `Items, servicesToTable (`complex));
  1239.             // message popup
  1240.             Popup::Message (_("Each service was enabled\nin the appropriate runlevels."));
  1241.             }
  1242.         }
  1243.         else if (`start == ret || `stop == ret)
  1244.         {
  1245.             boolean really = true;
  1246.             if (`stop == ret && "xdm" == current_service)
  1247.             {
  1248.             // yes-no popup. the user wants to stop xdm
  1249.             if (!Popup::YesNo ( _("This may kill your X session.\n\nProceed?")))
  1250.             {
  1251.                 really = false;
  1252.             }
  1253.             }
  1254.             if (really)
  1255.             {
  1256.             StartStopServiceDep (current_service, ret == `start);
  1257.             }
  1258.         }
  1259.         else if (`status == ret)
  1260.         {
  1261.             // similar to startStopService but there will be changes
  1262.             // when dependencies are checked
  1263.  
  1264.             //TODO: find a place for it
  1265.             //Popup::Message (overviewText ());
  1266.             list r = startStopService (current_service, "status");
  1267.             Popup::LongText ("", `RichText(r[1]:""), 70, 5);
  1268.         }
  1269.         }
  1270.  
  1271.     Wizard::RestoreScreenShotName ();
  1272.     return (symbol) ret;
  1273.     }
  1274.  
  1275.     // simple mode
  1276.     /**
  1277.      * Main dialog for changing services.
  1278.      * @return symbol for wizard sequencer
  1279.      */
  1280.     define symbol SimpleDialog () ``{
  1281.     Wizard::SetScreenShotName ("runlevel-2-simple");
  1282.     map service = $[];
  1283.  
  1284.     string help_text =
  1285.         // Simple mode dialog
  1286.         // help text
  1287.         _("<p>Here, specify which system services should be started.</p>")
  1288.         +
  1289.         // warning
  1290.         _("<p><b>Warning:</b> The system services (runlevel editor) is an expert tool. Only change settings if you know
  1291.  what you are doing.  Otherwise your system might not function properly afterwards.</p>
  1292. ")
  1293.         +
  1294.         // help text
  1295.         // Activate is a button
  1296.         _("<p><b>Activate</b> starts the selected service and services
  1297. that it depends on and enables them to start at system boot time.
  1298. Likewise, <b>Deactivate</b> stops services that depend on a given service
  1299. and the service itself and disables their start at system boot time.</p>")
  1300.         +
  1301.         // help text
  1302.         _("<p>An asterisk (*) after a service status means that the service is enabled but not running or is disabled but running now.</p>")
  1303.         +
  1304.         // help text
  1305.         _("<p>To change the behavior of runlevels and system services in detail, click <b>Expert Mode</b>.</p>\n")
  1306.         ;
  1307.  
  1308.     term contents = `VBox (
  1309.         `VSpacing (0.4),
  1310.         ModeTabs (`simple),
  1311.         `VSpacing (0.4),
  1312.         `Table (`id (`table), `opt (`notify, `immediate),
  1313.             `header (_("Service"), _("Enabled"), _("Description")),
  1314.             servicesToTable (`simple)
  1315.         ),
  1316.         `VSquash (
  1317.         `HBox (
  1318.             `VSpacing (4.3), // 3+borders in qt, 3 in curses
  1319.             `RichText (`id (`description), `opt (`shrinkable, `vstretch), "")
  1320.             )
  1321.         ),
  1322.         `HBox (
  1323.         // Button label
  1324.         `PushButton (`id (`enable), `opt (`key_F3),  _("Ena&ble")),
  1325.         // Button label
  1326.         `PushButton (`id (`disable), `opt (`key_F5), _("&Disable"))
  1327.         )
  1328.         );
  1329.     // dialog caption.
  1330.     Wizard::SetContentsButtons (_("System Services (Runlevel): Services"), contents, help_text, Label::BackButton(), Label::FinishButton());
  1331.     Wizard::DisableBackButton ();
  1332.  
  1333.     UI::ChangeWidget (`id (`table), `CurrentItem, current_service);
  1334.     service = RunlevelEd::services[current_service]:$[];
  1335.     updateDescription (service);
  1336.  
  1337.     
  1338.     UI::SetFocus (`id (`table));
  1339.     any ret = nil;
  1340.     boolean focustable = false;
  1341.  
  1342.     // fetch service which were not checked before
  1343.     fetching_service_status = true;
  1344.     fetching_service_index  = 0;
  1345.  
  1346.     while (`next != ret && `back != ret && `abort != ret && `complex != ret)
  1347.     {
  1348.         if (ret != nil) y2milestone("RET: %1", ret);
  1349.         if (focustable) {
  1350.         // Kludge, because a `Table still does not have a shortcut.
  1351.         // #16116
  1352.         UI::SetFocus (`id (`table));
  1353.         }
  1354.         
  1355.         if (fetching_service_status)
  1356.         {
  1357.         ret = UI::PollInput ();
  1358.         UI::BusyCursor ();
  1359.         if (nil == ret)
  1360.         {
  1361.             serviceStatusIterator (true);
  1362.             continue ;
  1363.         }
  1364.         }
  1365.         else
  1366.         {
  1367.         ret = UI::UserInput ();
  1368.         focustable = true;
  1369.         }
  1370.         if (ret == `cancel)
  1371.         {
  1372.         ret = `abort;
  1373.         }
  1374.  
  1375.         current_service = (string) UI::QueryWidget (`id (`table), `CurrentItem);
  1376.  
  1377.         if (`abort == ret)
  1378.         {
  1379.         if (!reallyAbort ())
  1380.         {
  1381.             ret = nil;
  1382.             continue;
  1383.         }
  1384.         }
  1385.         else if (`next == ret)
  1386.         {
  1387.         // FIXME copied from ComplexDialog, make it a function
  1388.         // Misaligned for consistency with the original
  1389.  
  1390.             // TODO: check dependencies of all services? (on demand?)
  1391.             // string nfs_adj = RunlevelEd::CheckPortmap ();
  1392.             // ...
  1393.  
  1394.             if (RunlevelEd::isDirty ())
  1395.             {
  1396.             // yes-no popup
  1397.             if (!Popup::YesNo (_("Now the changes to runlevels \nwill be saved.")))
  1398.             {
  1399.                 ret = nil;
  1400.                 continue;
  1401.             }
  1402.             }
  1403.             break;
  1404.         }
  1405.         else if (`table == ret)
  1406.         {
  1407.         service = RunlevelEd::services[current_service]:$[];
  1408.         updateDescription (service);
  1409.         }
  1410.         else if (`disable == ret)
  1411.         { //FIXME...
  1412.         y2milestone ("%1", ret);
  1413.         SimpleSetServiceDep (current_service, false);
  1414.         }
  1415.         else if (`enable == ret)
  1416.         { //FIXME...
  1417.         y2milestone ("%1", ret);
  1418.         SimpleSetServiceDep (current_service, true);
  1419.         }
  1420.     }
  1421.     Wizard::RestoreScreenShotName ();
  1422.     return (symbol) ret;
  1423.     }
  1424.  
  1425.  
  1426.     // Autoyast UI
  1427.     /**
  1428.      * Help text for auto-complex-screen
  1429.      * @return help text
  1430.      */
  1431.     define string getHelpAuto () ``{
  1432.     return
  1433.         // help text
  1434.         _("<p><b>Prepare data for autoinstallation.</b></p>")
  1435.         +
  1436.         // help text
  1437.         _("<p>Change the services to requested state. Only services marked as changed will really be changed in the target system.</p>")
  1438.         +
  1439.         // help text
  1440.         _("<p>If you made a mistake and want to undo the change, press <b>Clear</b> or <b>Clear all</b>.</p>")
  1441.         ;
  1442.     }
  1443.     /**
  1444.      * Add service by hand.
  1445.      * @return new service name (already added to RunlevelEd::services) or ""
  1446.      */
  1447.     define string addService () ``{
  1448.     UI::OpenDialog (`VBox (
  1449.                 // dialog heading
  1450.             `Heading (`opt (`hstretch), _("Add service")),
  1451.             `VSpacing (1),
  1452.             // text entry
  1453.             `TextEntry (`id (`name), _("Service &name")),
  1454.             // label
  1455.             `Label (`opt (`hstretch), _("Starts in these runlevels by default:")),
  1456.             getRlCheckBoxes (),
  1457.             `VSpacing (1),
  1458.             // text entry
  1459.             `TextEntry (`id (`des), _("&Description (optional)"), ""),
  1460.             `VSpacing (1),
  1461.             `HBox (`PushButton (`id (`ok), `opt (`default, `key_F10),
  1462.                     Label::OKButton ()),
  1463.                `PushButton (`id (`cancel), `opt (`key_F9),
  1464.                     Label::CancelButton ()))
  1465.             ));
  1466.     UI::SetFocus (`id (`name));
  1467.  
  1468.     any ret = nil; // (symbol|string)
  1469.     string name = "";
  1470.     while (true)
  1471.     {
  1472.         ret = UI::UserInput ();
  1473.         if (`ok == ret)
  1474.         {
  1475.         name = (string) UI::QueryWidget (`id (`name), `Value);
  1476.         if (nil == name || "" == name || haskey (RunlevelEd::services, name))
  1477.         {
  1478.             // message popup
  1479.             Popup::Message (_("Invalid service name. You did not specify service
  1480. name or the name specified is already in use."));
  1481.             continue;
  1482.         }
  1483.         list def = [];
  1484.         foreach (string i, RunlevelEd::runlevels, ``{
  1485.             if ((boolean) UI::QueryWidget (`id (i), `Value))
  1486.             {
  1487.             def[size(def)] = i;
  1488.             }
  1489.         });
  1490.         map m = $[
  1491.             "dirty" : true,
  1492.             "defstart" : def,
  1493.             "start" : def,
  1494.             "description" : UI::QueryWidget (`id (`des), `Value)
  1495.             ];
  1496.         RunlevelEd::services[name] = m;
  1497.         break;
  1498.         }
  1499.         if (`cancel == ret)
  1500.         {
  1501.         name = "";
  1502.         break;
  1503.         }
  1504.     }
  1505.     UI::CloseDialog ();
  1506.     return name;
  1507.     }
  1508.  
  1509.     /**
  1510.      * Main dialog for changing services.
  1511.      * @return symbol for wizard sequencer
  1512.      */
  1513.     define symbol AutoDialog () ``{
  1514.     Wizard::SetScreenShotName ("runlevel-2-auto");
  1515.     // currently selected service we are working with
  1516.     map service = $[];
  1517.  
  1518.     /**
  1519.      * Sets columns 0-S (runlevels) in table so they are synchronized with checkboxes.
  1520.      */
  1521.     define void refreshTableLine2 () ``{
  1522.         updateRlColumns (current_service, service, nil);
  1523.         UI::ChangeWidget (`id (`table), `Item (current_service, c_dirty),
  1524.                   service["dirty"]:false ? UI::Glyph(`CheckMark) : " ");
  1525.     }
  1526.  
  1527.     // headers in table
  1528.     term header = `header (
  1529.         // table header
  1530.         _("Service"),
  1531.         // table header. has the service state changed?
  1532.         _("Changed"));
  1533.     foreach (string i, RunlevelEd::runlevels, ``{
  1534. //        header = add (header, `Center (" " + i + " "));
  1535.         header = add (header, `Center (i));
  1536.     });
  1537.     // headers in table
  1538.     header = add (header, _("Description"));
  1539.     term contents = `VBox (
  1540.         `VSpacing (0.4),
  1541.         // combo box label
  1542.         `ComboBox (`id (`default_rl), `opt (`hstretch), _("&Set default runlevel after booting to:"), RunlevelEd::getDefaultPicker (`auto)),
  1543.  
  1544.         `VSpacing (0.4),
  1545.         `Table (`id (`table), `opt (`notify, `immediate),
  1546.             header,
  1547.             servicesToTable (`auto)
  1548.         ),
  1549.         `VBox (
  1550.         // label above checkboxed
  1551.         `Label (`id (`service_label), `opt (`hstretch), _("Service will be started in following runlevels:")),
  1552.         getRlCheckBoxes ()
  1553.         ),
  1554.         `HBox (
  1555.         // button label
  1556.         `PushButton (`id (`add), `opt (`key_F3), _("A&dd")),
  1557.         `HStretch (),
  1558.         // button label
  1559.         `PushButton (`id (`clear), _("&Clear")),
  1560.         // button label
  1561.         `PushButton (`id (`clear_all), _("Clea&r All")),
  1562.         // button label
  1563.         `PushButton (`id (`default), _("D&efault"))
  1564.         )
  1565.         );
  1566.     // dialog caption.
  1567.     Wizard::SetContentsButtons (_("System Services (Runlevel): Details"), contents, getHelpAuto (), Label::BackButton(), Label::FinishButton());
  1568.     Wizard::DisableBackButton ();
  1569.  
  1570.     UI::ChangeWidget (`id (`table), `CurrentItem, current_service);
  1571.     service = RunlevelEd::services[current_service]:$[];
  1572.     updateRlCheckBoxes (service);
  1573.     any ret = nil;
  1574.     while (nil != UI::PollInput ()) {sleep(50);}
  1575.     while (`next != ret && `back != ret && `abort != ret)
  1576.         {
  1577.         // Kludge, because a `Table still does not have a shortcut.
  1578.         // #16116
  1579.         UI::SetFocus (`id (`table));
  1580.  
  1581.         ret = UI::UserInput ();
  1582.         if (ret == `cancel)
  1583.         {
  1584.             ret = `abort;
  1585.         }
  1586.  
  1587.         if (`abort == ret)
  1588.         {
  1589.             if (!reallyAbort ())
  1590.             {
  1591.             ret = nil;
  1592.             continue;
  1593.             }
  1594.         }
  1595.         else if (`next == ret)
  1596.         {
  1597.             //FIXME dependencies none or proper
  1598.  
  1599.             string nfs_adj = RunlevelEd::CheckPortmap ();
  1600.             if (nil != nfs_adj)
  1601.             {
  1602.             UI::ChangeWidget (`id (`table), `CurrentItem, "portmap");
  1603.             current_service = "portmap";
  1604.             service = RunlevelEd::services["portmap"]:$[];
  1605.             updateRlCheckBoxes (service);
  1606.             while (nil != UI::PollInput ()) {sleep(50);}
  1607.             // yes-no popup
  1608.             if (!Popup::YesNo (sformat (_("Service portmap, which is required by
  1609. %1, is disabled. Enable
  1610. portmap if you want to run %1.
  1611.  
  1612. Leave portmap
  1613. disabled?\n"), nfs_adj)))
  1614.             {
  1615.                 ret = nil;
  1616.                 continue;
  1617.             }
  1618.             }
  1619.             RunlevelEd::default_runlevel = (string) UI::QueryWidget (`id (`default_rl), `Value);
  1620.             break;
  1621.         }
  1622.         else if (`add == ret)
  1623.         {
  1624.             string name = addService ();
  1625.             if ("" != name)
  1626.             {
  1627.             UI::ChangeWidget (`id (`table), `Items, servicesToTable (`auto));
  1628.             UI::ChangeWidget (`id (`table), `CurrentItem, name);
  1629.             // qt and curses behave differently:
  1630.             // one of them sends notification after changewidget
  1631.             // and the other does not.
  1632.             // So eat it.
  1633.             while (nil != UI::PollInput ()) {sleep(50);}
  1634.             ret = `table;
  1635.             }
  1636.         }
  1637.         else if (`default == ret)
  1638.         {
  1639.             setServiceToDefault (current_service);
  1640.             service = RunlevelEd::services[current_service]:$[];
  1641.             refreshTableLine2 ();
  1642.             ret = `table;
  1643.         }
  1644.         else if (`clear == ret)
  1645.         {
  1646.             // re-read from SCR
  1647.             service = Service::Info (current_service);
  1648.             RunlevelEd::services[current_service] = service;
  1649.             refreshTableLine2 ();
  1650.             ret = `table;
  1651.         }
  1652.         else if (`clear_all == ret)
  1653.         {
  1654.             RunlevelEd::ClearServices ();
  1655.             UI::ChangeWidget (`id (`table), `Items, servicesToTable (`auto));
  1656.             ret = `table;
  1657.         }
  1658.         else if (nil != ret && is (ret, string))
  1659.         {
  1660.             // checkbox pressed
  1661.             // checked or unchecked?
  1662.             string checked = (((boolean) UI::QueryWidget (`id (ret), `Value)) ? (string) ret : " ");
  1663.             service = queryRlCheckBoxes (current_service, service);
  1664.             UI::ChangeWidget (`id (`table), `Item (current_service, runlevel2tableindex[ret]:-1), checked);
  1665.             UI::ChangeWidget (`id (`table), `Item (current_service, c_dirty), UI::Glyph(`CheckMark));
  1666.         }
  1667.  
  1668.         // not a part of the else-if chain above!
  1669.         if (`table == ret)
  1670.         {
  1671.             current_service = (string) UI::QueryWidget (`id (`table), `CurrentItem);
  1672.             service = RunlevelEd::services[current_service]:$[];
  1673.             updateRlCheckBoxes (service);
  1674.             while (nil != UI::PollInput ()) {sleep(50);}
  1675.         }
  1676.         }
  1677.     Wizard::RestoreScreenShotName ();
  1678.     return (symbol) ret;
  1679.     }
  1680.  
  1681.     // generic ui helpers
  1682.     /**
  1683.      * Like Popup::LongText
  1684.      * @param headline    a headline
  1685.      * @param richtext    `RichText(_("<p>foo...</p>"))
  1686.      * @param hdim    popup width
  1687.      * @param vdim    popup height
  1688.      * @return continue?
  1689.      */
  1690.     define boolean LongContinueCancelHeadlinePopup (
  1691.     string headline, term richtext, integer hdim, integer vdim) ``{
  1692.  
  1693.     UI::OpenDialog (
  1694.         `opt (`decorated),
  1695.         `HBox (
  1696.         `VSpacing (vdim),
  1697.         `VBox (
  1698.             `HSpacing (hdim),
  1699.             `Left (`Heading (headline)),
  1700.             `VSpacing (0.2),
  1701.             richtext,    // scrolled text
  1702.             `HBox (
  1703.             `PushButton (
  1704.                 `id (`continue),
  1705.                 `opt (`default, `key_F10),
  1706.                 Label::ContinueButton ()),
  1707.             `PushButton (
  1708.                 `id (`cancel),
  1709.                 `opt (`key_F9),
  1710.                 Label::CancelButton ())
  1711.             )
  1712.             )
  1713.         )
  1714.         );
  1715.  
  1716.     UI::SetFocus (`id (`continue));
  1717.  
  1718.     boolean ret = UI::UserInput() == `continue;
  1719.     UI::CloseDialog();
  1720.     return ret;
  1721.     }
  1722.  
  1723.     // move them to the module
  1724.     // (no ui interaction)
  1725.     /**
  1726.      * Disable the service. Changes global services.
  1727.      * @param service_name name of the service.
  1728.      */
  1729.     define void setServiceDisable (string service_name) ``{
  1730.     map service = RunlevelEd::services[service_name]:$[];
  1731.     RunlevelEd::services[service_name] = union (service,
  1732.                    $[
  1733.                    "start": [],
  1734.                    "dirty": true,
  1735.                    ]);
  1736.     }
  1737.     /**
  1738.      * DUH, in fact ENABLES  the service.
  1739.      * but the described function will be there sometime
  1740.      *
  1741.      * Set service to its default state upon installation.
  1742.      * Changes global services.
  1743.      * @param service_name Name of service to process.
  1744.      */
  1745.     define void setServiceToDefault (string service_name) ``{
  1746.     map service = RunlevelEd::services[service_name]:$[];
  1747.     RunlevelEd::services[service_name] = union (service,
  1748.                    $[
  1749.                    "start": service["defstart"]:[],
  1750.                    "dirty": true,
  1751.                    ]);
  1752.     }
  1753.  
  1754.     // favorite missing builtins
  1755.     /**
  1756.      * Converts a list to a map with values of true
  1757.      * @param l a list
  1758.      * @return a map
  1759.      */
  1760.     define map<string, boolean> tomap_true (list<string> l) ``{
  1761.     return (map<string, boolean>) listmap (string i, l, ``( $[i: true] ));
  1762.     }
  1763.  
  1764.     /**
  1765.      * @param m a map
  1766.      * @return keys of the map
  1767.      */
  1768.     define list mapkeys (map m) ``{
  1769.     return maplist (any k, any v, m, ``( k ));
  1770.     }
  1771.  
  1772.  
  1773. }
  1774.