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 / RunlevelEd.ycp < prev    next >
Text File  |  2006-11-29  |  27KB  |  942 lines

  1. /**
  2.  * File:
  3.  *   RunlevelEd.ycp
  4.  * Package:
  5.  *   System Services (Runlevel) (formerly known as Runlevel Editor)
  6.  * Summary:
  7.  *   Data for configuration of services, input and output functions.
  8.  *
  9.  * Authors:
  10.  *   Martin Vidner <mvidner@suse.cz>
  11.  *   Petr Blahos <pblahos@suse.cz>
  12.  *   Martin Lazar <mlazar@suse.cz>
  13.  *
  14.  * $Id: RunlevelEd.ycp 33299 2006-10-10 09:39:23Z locilka $
  15.  */
  16.  
  17. {
  18.     module "RunlevelEd";
  19.     textdomain "runlevel";
  20.  
  21.     import "Service";
  22.     import "Progress";
  23.     import "Summary";
  24.     import "Report";
  25.     import "CommandLine";
  26.  
  27.     include "runlevel/toposort.ycp";
  28.  
  29.     /**
  30.      * Proposal parameter: if it changes, we repropose
  31.      */
  32.     global boolean x11_selected = nil;
  33.  
  34.     /**
  35.      * Like "requires" but in reverse direction.
  36.      * Used for stopping and disabling services.
  37.      */
  38.     map<string, list<string> >  what_requires = $[];
  39.  
  40.     /**
  41.      * A graph where nodes are scripts or system facilities
  42.      * but not normal facilities (ie. provides are solved).
  43.      */
  44.     map<string, list<string> >  requires = $[];
  45.  
  46.     define void buildRequires ();
  47.     define void addRequires (string service, list<string> req_facilities);
  48.     define list reverse (list l);
  49.     global define boolean StartContainsImplicitly (list<string> rls, string rl);
  50.     define boolean ImplicitlySubset (list<string> rls_a, list<string> rls_b);
  51.     define boolean subset (list a, list b);
  52.  
  53.     /**
  54.      * @struct service
  55.      * One service is described by such map: <pre>
  56.       "servicename" : $[
  57.     "defstart" : [ "2", "3", "5", ], // Default-Start comment
  58.     "defstop"  : [ "0", "1", "6", ], // Default-Stop  comment
  59.  
  60.     // "should" dependencies (+) are filtered by addRequires
  61.     "reqstart" : [ "$network", "portmap" ], // Required-Start comment
  62.     "reqstop"  : [ "$network", "portmap" ], // Required-Stop  comment
  63.  
  64.     "shortdescription" : "text...",       // Description comment
  65.     "description" : "text...",       // Description comment
  66.  
  67.     // which runlevels service is really started/stopped in
  68.     // read from /etc/init.d/{rc?,boot}.d/* links
  69.     //
  70.     // Note that the boot process (init.d/boot.d) is considered
  71.     // a "B" runlevel that is implicitly contained in the other runlevels.
  72.     // Using
  73.     //   list st = services ["boot.local", "start"]:[]
  74.     //   contains (st, "3") // DON'T
  75.     // results in false but that's probably not what you want.
  76.     // Use
  77.     //   StartContainsImplicitly (st, "3")
  78.     // which tests for "3" and "B".
  79.     "start" : [ "3", "5", ],
  80.     "stop"  : [ "3", "5", ],
  81.  
  82.     "started" : 0, // return from rcservice status (integer)
  83.  
  84.     "dirty" : false, // was the entry changed?
  85.       ]</pre>
  86.      */
  87.     //
  88.     //
  89.  
  90.  
  91.     /**
  92.      * List of all services. Each item is a map described above.
  93.      * @ref service
  94.      */
  95.     global map<string,map> services = $[];
  96.  
  97.     /**
  98.      * List of all service names.
  99.      * Filled by Read, used to get all services' status.
  100.      */
  101.     global list service_list = [];
  102.  
  103.     /**
  104.      * Default runlevel (after boot)
  105.      */
  106.     global string default_runlevel = "";
  107.  
  108.     /**
  109.      * Backup of default runlevel.
  110.      */
  111.     string default_orig = "";
  112.  
  113.     /**
  114.      * List of all runlevels available in the system.
  115.      */
  116.     global list<string> runlevels = [];
  117.  
  118.     /**
  119.      * Current runlevel
  120.      */
  121.     global string current = "";
  122.  
  123.     /* Dependency solving: */
  124.     /**
  125.      * ONLY ONE SCRIPT provides a facility in this model.
  126.      * In SuSE packages, the only exception are sendmail and postfix
  127.      * both providing sendmail but they cannot be installed together
  128.      * anyway.
  129.      * atd has Provides: at, so
  130.      *   what_provides["at"] == "atd";
  131.      * Identity is not represented explicitly: ypbind has Provides: ypbind, but
  132.      *   haskey (what_provides, "ypbind") == false;
  133.      */
  134.     map what_provides = $[];
  135.  
  136.     /**
  137.      * System facility definitions
  138.      * "should" dependencies (+) are filtered by addRequires
  139.      * /etc/insserv.conf:
  140.      *   system_requires["$network"] == ["network", "+pcmcia", "+hotplug"];
  141.      */
  142.     map<string, list<string> > system_requires = $[];
  143.  
  144.     /**
  145.      * Read settings
  146.      * @return success
  147.      */
  148.     global define boolean Read () ``{
  149.     // progress caption
  150.     Progress::Simple (_("Initializing system services (runlevel). Please wait..."), " ", 7, "");
  151.     Progress::NextStep ();
  152.     runlevels= (list<string>) SCR::Read (.init.scripts.runlevel_list);
  153.     if (0 == size (runlevels))
  154.     {
  155.         runlevels = ["0", "1", "2", "3", "5", "6", "S", ];
  156.     }
  157.     Progress::NextStep ();
  158.  
  159.     current = (string) SCR::Read (.init.scripts.current_runlevel);
  160.     Progress::NextStep ();
  161.  
  162.     //..
  163.     default_runlevel = (string) SCR::Read (.init.scripts.default_runlevel);
  164.     default_orig = default_runlevel;
  165.     Progress::NextStep ();
  166.  
  167.     system_requires = (map<string, list<string> >) SCR::Read (.init.insserv_conf);
  168.     Progress::NextStep ();
  169.  
  170.     map details = (map) SCR::Read (.init.scripts.runlevels);
  171.     Progress::NextStep ();
  172.     services = (map<string,map>) SCR::Read (.init.scripts.comments);
  173.     Progress::NextStep ();
  174.     services = (map<string, map>) mapmap (string k, map v, services, ``{
  175.         foreach (string f, v["provides"]:[], ``{
  176.         // identity implicit; only the first script provides a facility
  177.         if (f != k && !haskey (what_provides, f))
  178.         {
  179.             what_provides[f] = k;
  180.         }
  181.         });
  182.  
  183.         service_list[size(service_list)] = k;
  184.  
  185.  
  186.         // play tennis
  187.         map second_service = details[k]:$[];
  188.         v["start"] = second_service["start"]:[];
  189.         v["stop"] = second_service["stop"]:[];
  190.         return $[ k: v ];
  191.     });
  192.     buildRequires ();
  193.     what_requires = ReverseGraph (requires);
  194.     Progress::NextStep ();
  195.     return true;
  196.     }
  197.  
  198.     /**
  199.      * If there's a dependency loop, dependency checking is disabled.
  200.      */
  201.     boolean dependencies_disabled = false;
  202.     /**
  203.      * Create requires from services, system_requires and what_provides.
  204.      */
  205.     define void buildRequires () ``{
  206.     foreach (string service, map comments, services, ``{
  207.         addRequires (service, (list<string>) (comments["reqstart"]:[]));
  208.     });
  209.     foreach (string sys_f, list<string> req, system_requires, ``{
  210.         addRequires (sys_f, req);
  211.     });
  212.     }
  213.     /**
  214.      * Resolve provides, filter out "should" dependencies (+)
  215.      * and add the requirements to "requires".
  216.      * Missing services are not detected.
  217.      * @param service a service
  218.      * @param req_facilities its required facilities
  219.      */
  220.     define void addRequires (string service, list<string> req_facilities) ``{
  221.     list<string> req_s = filter (string f, req_facilities,
  222.                  ``( substring (f, 0, 1) != "+"));
  223.     req_s = maplist (string f, req_s, ``( what_provides[f]:f ));
  224.     requires[service] = req_s;
  225.     }
  226.  
  227.     /**
  228.      * Resolve which services need to be enabled/disabled
  229.      * @param service a service
  230.      * @param enable enabling or disabling a service?
  231.      * @return a list of services (excluding itself) required to start
  232.      * a service (enable) or to be stopped because they require the
  233.      * service (disable), ordered by their dependencies. Missing
  234.      * services are included, system facilities excluded.<br>
  235.      * If dependencies are disabled, returns an empty list, as if
  236.      * there were no dependencies.
  237.      */
  238.     global define list<string> ServiceDependencies (string service,
  239.                             boolean enable) ``{
  240.     if (dependencies_disabled)
  241.     {
  242.         return [];
  243.     }
  244.     // make a dependency subgraph
  245.     map<string, list<string> > s_req = ReachableSubgraph (enable? requires: what_requires,
  246.                        service);
  247.     y2debug ("DEPGRAPH %1: %2", service, s_req);
  248.     // sort it
  249.     list r = TopologicalSort (s_req);
  250.     list<string> sorted = r[0]:[];
  251.     list rest = r[1]:[];
  252.     if (size (rest) > 0)
  253.     {
  254.         // TODO: localize the loop, disable it only locally
  255.         // and say what scripts form it
  256.         Report::Error (_("A dependency loop was detected.
  257. Further dependency checking will be disabled."));
  258.         dependencies_disabled = true;
  259.     }
  260.  
  261.     // filter system facilities
  262.     sorted = filter (string f, sorted, ``(substring (f, 0, 1) != "$"));
  263.     // remove the original service
  264.     sorted = remove (sorted, 0);
  265.     // reverse it so that the required services are first
  266.     return (list<string>) reverse (sorted);
  267.     }
  268.  
  269.     /**
  270.      * Argh, not a builtin
  271.      * @param l a list
  272.      * @return reversed list
  273.      */
  274.     define list reverse (list l) ``{
  275.     if (l == nil)
  276.     {
  277.         return nil;
  278.     }
  279.     list result = [];
  280.     foreach (any item, l, ``{
  281.         result = prepend (result, item);
  282.     });
  283.     return result;
  284.     }
  285.  
  286.     /**
  287.      * Gets a list of dependent services and a target state they
  288.      * should be in. Filters out those that are already in the target
  289.      * state.
  290.      * If both init_time and run_time are on, a conjunction is needed.
  291.      * @param svcs    dependent services
  292.      * @param rls    used for init_time
  293.      * @param enable    on/off:
  294.      * @param init_time enable/disable
  295.      * @param run_time  start/stop
  296.      */
  297.     global define list<string> FilterAlreadyDoneServices (list<string> svcs,
  298.                               list<string> rls,
  299.                               boolean enable,
  300.                               boolean init_time,
  301.                               boolean run_time) ``{
  302.  
  303.     // one: exactly one runlevel. nil means (disable) in all runlevels
  304.     boolean one = rls != nil && size (rls) == 1;
  305.     if (init_time && !enable &&
  306.         rls != nil && !one)
  307.     {
  308.         // should not happen
  309.         y2error ("Disabling in a nontrivial set of runlevels (%1) not implemented.", rls);
  310.         return nil;
  311.     }
  312.     string rl = rls[0]:"";
  313.  
  314.     return filter (string service, svcs, ``{
  315.         boolean all_ok = nil; // is the service in the target state?
  316.  
  317.         // run_time
  318.         boolean run_ok = true;
  319.         if (run_time)
  320.         {
  321.         boolean started =
  322.             services[service, "started"]:-1 == 0 ||
  323.             // boot scripts are always considered to be started,
  324.             // even if they return 4 :(
  325.             services[service, "defstart"]:[] == [ "B" ] ||
  326.             // and while we're at it with kludges,
  327.             // pretend nfs is running (ie. /usr is available)
  328.             // because it reports 3 when no nfs imports are defined :(
  329.             // TODO resolve it better!
  330.             service == "nfs";
  331.         run_ok = started == enable;
  332.         }
  333.  
  334.         // init_time
  335.         boolean init_ok = true;
  336.         if (init_time)
  337.         {
  338.         list<string> start = services[service, "start"]:[];
  339.  
  340.         if (one)
  341.         {
  342.             init_ok = enable == StartContainsImplicitly (start, rl);
  343.         }
  344.         else
  345.         {
  346.             if (enable)
  347.             {
  348.             init_ok = ImplicitlySubset (rls, start);
  349.             }
  350.             else
  351.             {
  352.             // rls is nil, we only support disabling
  353.             // in one or all runleves at once
  354.             init_ok = start == [];
  355.             }
  356.         }
  357.         }
  358.  
  359.  
  360.         // keep it in the list if something needs to be done
  361.         return !(init_ok && run_ok);
  362.     });
  363.     }
  364.  
  365.  
  366.     /**
  367.      * Is a service started in a runlevel, given the list of rulevels
  368.      * it is started in?
  369.      * This looks like a simple contains,
  370.      * but "B" implicitly expands to all runlevels.
  371.      * See also bug #17234.
  372.      * @param rls runlevels the service is started in
  373.      * @param rl  which runlevel is tested
  374.      * @return should it be running in rl?
  375.      */
  376.     global define boolean StartContainsImplicitly (list<string> rls,
  377.                            string rl) ``{
  378.     return contains (rls, "B") || contains (rls, rl);
  379.     }
  380.     /**
  381.      * Whether a set of runlevels is a subset of another set of runlevels.
  382.      * But expands "B" to the whole set
  383.      */
  384.     define boolean ImplicitlySubset (list<string> rls_a,
  385.                         list<string> rls_b) ``{
  386.     return contains (rls_b, "B") || subset (rls_a, rls_b);
  387.     }
  388.  
  389.     /**
  390.      * @param a a set
  391.      * @param b a set
  392.      * @return a \subseteq b
  393.      */
  394.     define boolean subset (list a, list b) ``{
  395.     return size (union (a, b)) <= size (b);
  396.     }
  397.  
  398.     /**
  399.      * Set all dirty services as clean and tries to read
  400.      * original "start"/"stop" for them.
  401.      */
  402.     global define void ClearServices () ``{
  403.     services = (map<string, map>)mapmap (string k, map v, services, ``{
  404.         if (v["dirty"]:false)
  405.         {
  406.         v["dirty"] = false;
  407.         map r = (map) SCR::Read (.init.scripts.runlevel, k);
  408.         r = r[k]:$[];
  409.         v["start"] = r["start"]:[];
  410.         v["stop"] = r["stop"]:[];
  411.         }
  412.         return $[k: v];
  413.     });
  414.     }
  415.  
  416.     /**
  417.      * Is a service disabled?
  418.      * Checks whether the default runlevel is in the list of runlevels
  419.      * @param service service to check
  420.      * @return boolean true if service is disabled
  421.      */
  422.     global define boolean isDisabled (map service) ``{
  423.     return !contains(service["start"]:[], default_runlevel);
  424.     }
  425.  
  426.     /**
  427.      * Check for portmap. Portmap should be started if inetd, nfs,
  428.      * nfsserver, nis, ... is started. This checks the dependency.
  429.      * @return string name of the first enabled service that requires portmap
  430.      */
  431.     global define string CheckPortmap () ``{
  432.     if (!isDisabled (services["portmap"]:$[]))  // if portmap is enabled, there is no problem
  433.         return nil;
  434.     string req = nil;
  435.     list in = [];
  436.     foreach (string k, map v, services, ``{
  437.         if (contains (v["reqstart"]:[], "portmap") && size (v["start"]:[]) > 0)
  438.         {
  439.         in = union (in, toset (v["start"]:[]));
  440.         if (nil == req)
  441.             req = k;
  442.         }
  443.     });
  444.     return size (in) > 0 ? req : nil;
  445.     }
  446.  
  447.     /**
  448.      * Save changed services into proper runlevels. Save also changed
  449.      * default runlevel.
  450.      * @return success
  451.      */
  452.     global define boolean Write () ``{
  453.     integer prsize = size (services);
  454.     // progress caption
  455.     Progress::Simple (_("Saving changes to runlevels."), " ", prsize + 1, "");
  456.  
  457.     if (default_runlevel != default_orig)
  458.         SCR::Write (.init.scripts.default_runlevel, default_runlevel);
  459.     Progress::NextStep ();
  460.  
  461.     map<string,map> failed = (map<string, map>)filter (string k, map v, services, ``{
  462.         boolean fail = false;
  463.         if (v["dirty"]:false)
  464.         {
  465.         // progress item, %1 is a service (init script) name
  466.         Progress::Title (sformat (_("Service %1"), k));
  467.         // save!
  468.         list start = v["start"]:[];
  469.         y2milestone ("Setting %1: %2", k, start);
  470.         string verbose = sformat(_("Setting %1: %2"), k, start);
  471.         CommandLine::PrintVerbose(verbose);
  472.         fail = ! Service::Finetune (k, start);
  473.         }
  474.         else
  475.         // progress item, %1 is a service (init script) name
  476.         Progress::Title (sformat (_("Skipping service %1."), k));
  477.         Progress::NextStep ();
  478.         return fail;
  479.     });
  480.     Progress::NextStep ();
  481.     string failed_s = mergestring ((list<string>) maplist (string k, any v, failed, ``(k)), ", ");
  482.     if (size (failed_s) > 0)
  483.     {
  484.         Report::Error (sformat (_("Failed services: %1."), failed_s));
  485.         return false;
  486.     }
  487.     return true;
  488.     }
  489.  
  490.     /**
  491.      * Were some settings changed?
  492.      * @return true if yes
  493.      */
  494.     global define boolean isDirty () ``{
  495.     if (default_runlevel != default_orig)
  496.         return true;
  497.  
  498.     boolean dirty = false;
  499.     foreach (string k, map v, services, ``{
  500.         if (dirty)
  501.         {
  502.         return ;
  503.         }
  504.         if (v["dirty"]:false)
  505.         {
  506.         dirty = true;
  507.         }
  508.     });
  509.     return dirty;
  510.     }
  511.  
  512.     /**
  513.      * Returns true if the settings were modified
  514.      * @return settings were modified
  515.      */
  516.     global boolean GetModified () {
  517.     return isDirty ();
  518.     }
  519.  
  520.     /**
  521.      * Function sets an internal variable indicating that any
  522.      * settings were modified to "true".
  523.      * Used for autoinst cloning.
  524.      */
  525.     global void SetModified () {
  526.     // Make sure GetModified will return true
  527.     default_orig = "---";
  528.     // maybe we should also touch dirty for all services,
  529.     // but that depends on what autoinst Clone really wants
  530.     }
  531.  
  532.     /**
  533.      * Export user settings.
  534.      * @return user settings:<pre>$[
  535.      *    "services": $[ map of dirty services ],
  536.      *    "default":  the default runlevel, if changed,
  537.      *]</pre>
  538.      */
  539.     global define map Export () ``{
  540.         y2debug("services: %1", services);
  541.     map<string,map> svc = (map<string, map>)filter (string k, map v, services, ``{
  542.         return v["dirty"]:false;
  543.     });
  544.     list tmp_services = maplist(string service_name, map service_data, svc, ``{
  545.         string  service_start = mergestring(service_data["start"]:[], " ");
  546.         string service_stop = mergestring(service_data["stop"]:[], " ");
  547.  
  548.         map service_map = $[];
  549.         service_map["service_name"] = service_name;
  550.         if (size(service_start) > 0 )
  551.         {
  552.         service_map["service_start"] = service_start;
  553.         }
  554.         if (size(service_stop) > 0 )
  555.         {
  556.         service_map["service_stop"] = service_stop;
  557.         }
  558.  
  559.         return (service_map);
  560.     });
  561.         map ret = $[];
  562.         if (size(tmp_services)>0)
  563.         {
  564.         ret = $[ "services" : tmp_services ];
  565.         }
  566.     if ( default_runlevel != "")
  567.     {
  568.         ret["default"] = default_runlevel;
  569.     }
  570.     return ret;
  571.     }
  572.     /**
  573.      * Import user settings
  574.      * @param s user settings
  575.      * @see Export
  576.      * @return success state
  577.      */
  578.     global define boolean Import (map s) ``{
  579.     runlevels = (list<string>) SCR::Read (.init.scripts.runlevel_list);
  580.     if (0 == size (runlevels))
  581.     {
  582.         runlevels = ["0", "1", "2", "3", "5", "6", "S", ];
  583.     }
  584.  
  585.     //..
  586.     default_runlevel = (string) SCR::Read (.init.scripts.default_runlevel);
  587.     default_orig = default_runlevel;
  588.  
  589.     // and finaly process map being imported
  590.     list<map> new = s["services"]:[];
  591.     map<string,map> tmp_services = (map<string, map>)listmap(map service, new,  ``{
  592.         string name = service["service_name"]:"";
  593.         list stop = [];
  594.         list start = [];
  595.         if (haskey(service, "service_status"))
  596.         {
  597.         if (service["service_status"]:"" == "enable")
  598.         {
  599.             map info = Service::Info(name);
  600.             y2milestone("service info for %1: %2", name, info);
  601.             start = info["defstart"]:[];
  602.             stop = info["defstop"]:[];
  603.         }
  604.         else if (service["service_status"]:"" == "disable")
  605.         {
  606.             start = [];
  607.             stop = [];
  608.         }
  609.         }
  610.         else
  611.         {
  612.         start = splitstring(service["service_start"]:"", " ");
  613.         stop = splitstring(service["service_stop"]:"", " ");
  614.         }
  615.  
  616.         map service_map = $[];
  617.         if (size(start) > 0 )
  618.         service_map["start"] = start;
  619.         if (size(stop) > 0 )
  620.         service_map["stop"] = stop;
  621.         return($[name:service_map]);
  622.     });
  623.  
  624.     if (size (tmp_services) > 0)
  625.     {
  626.         foreach (string k, map v, tmp_services, ``{
  627.         if (nil == services[k]:nil)
  628.         {
  629.             y2milestone ("Service %1 is not installed on target system, adding it by hand.", k);
  630.         }
  631.         v["dirty"] = true;
  632.         services[k] = v;
  633.         });
  634.     }
  635.     else
  636.         services = $[];
  637.     // default
  638.     if (haskey (s, "default"))
  639.     {
  640.         default_runlevel = s["default"]:"";
  641.         default_orig = "---";
  642.     }
  643.     return true;
  644.     }
  645.  
  646.     /**
  647.      * Returns textual runlevel description.
  648.      * Descriptions are hard-coded in ycp script.
  649.      * @param rl Runlevel to check.
  650.      * @return string Description.
  651.      */
  652.     global define string getRunlevelDescr (string rl) ``{
  653.     map<string, string> descr = $[
  654.         // descriptions of runlevels. there must be number: description
  655.         // number is runlevel name
  656.         // runlevel description
  657.         "0" : _("0: System halt"),
  658.         // runlevel description
  659.         "1" : _("1: Single user mode"),
  660.         // runlevel description
  661.         "2" : _("2: Local multiuser without remote network"),
  662.         // runlevel description
  663.         "3" : _("3: Full multiuser with network"),
  664.         // runlevel description
  665.         "4" : _("4: User defined"),
  666.         // runlevel description
  667.         "5" : _("5: Full multiuser with network and display manager"),
  668.         // runlevel description
  669.         "6" : _("6: System reboot"),
  670.         // runlevel description
  671.         // internal one: without a number
  672.         "" : _("Unchanged"),
  673.         ];
  674.     return descr[rl]:rl;
  675.     }
  676.  
  677.     /**
  678.      * @return Html formatted summary for the installation proposal
  679.      */
  680.     global define string ProposalSummary() ``{
  681.     string sum = "";
  682.     // summary header
  683.     sum = Summary::OpenList(sum);
  684.     sum = Summary::AddListItem(sum,  getRunlevelDescr (default_runlevel));
  685.     sum = Summary::CloseList(sum);
  686.  
  687.     return sum;
  688.     }
  689.  
  690.     /**
  691.      * @return Html formatted configuration summary
  692.      */
  693.     global define string Summary()
  694.     ``{
  695.     string sum = "";
  696.         sum = Summary::AddHeader(sum, _("Default Runlevel"));
  697.         sum = Summary::AddLine(sum,  getRunlevelDescr (default_runlevel));
  698.     // summary header
  699.     sum = Summary::AddHeader(sum, _("Services"));
  700.  
  701.     if (size(services)>0) {
  702.         sum = Summary::OpenList(sum);
  703.         foreach(string k, map v, services, ``{
  704.         if (v["dirty"]:false)
  705.         {
  706.             string item = sformat (
  707.             // summary item: %1 service name,
  708.             // %2, %3 list of runlevels it starts/stops in
  709.             _("<p><b>%1</b><br> Start: %2</p>"),
  710.             k,
  711.             mergestring(v["start"]:[], " ")
  712.             );
  713.             sum = Summary::AddListItem(sum, item );
  714.         }
  715.         });
  716.         sum = Summary::CloseList(sum);
  717.     }
  718.     else
  719.     {
  720.         sum =  Summary::AddLine(sum, Summary::NotConfigured());
  721.     }
  722.     return sum;
  723.     }
  724.  
  725.     // visualization helper
  726.  
  727.     /**
  728.      * A buffer for @ref sprint
  729.      */
  730.     string sprint_buffer = "";
  731.  
  732.     /**
  733.      * String print
  734.      * @param s a string to add to @ref sprint_buffer
  735.      */
  736.     define void sprint (string s) ``{
  737.     sprint_buffer = sprint_buffer + s;
  738.     }
  739.  
  740.     /**
  741.      * @return a graphviz graph of the service dependencies
  742.      */
  743.     global define string DotRequires () ``{
  744.     map in_attr = $[
  745.         "$remote_fs": "[color=yellow, minlen=2]",
  746.         "$local_fs": "[color=green, minlen=2]",
  747.         "$network": "[color=magenta, minlen=2]",
  748.         "$syslog": "[color=cyan, minlen=2]",
  749.         ];
  750.     sprint_buffer = "";
  751.     sprint ("digraph services {\n");
  752.     sprint ("\trankdir=LR;\n");
  753.     sprint ("\t\"!missing\"[rank=max];\n");
  754.     sprint ("\t\"$syslog\" -> \"$network\" [style=invis, minlen=10];\n");
  755.     sprint ("\t\"$remote_fs\" -> \"$syslog\" [style=invis, minlen=5];\n");
  756.     foreach (string n, list<string> e, requires, ``{
  757.         foreach (string target, e, ``{
  758.         string attr = in_attr[target]:"";
  759.         sprint (sformat ("\t\"%1\" -> \"%2\"%3;\n", n, target, attr));
  760.         });
  761.     });
  762.     sprint ("}\n");
  763.     return sprint_buffer;
  764.     }
  765.  
  766. /*** LiMaL API PART I. - Runlevels in /etc/inittab File ***/
  767.  
  768. global string GetCurrentRunlevel() {
  769.     return current == "" ? "unknown" : current;
  770. }
  771. global string GetDefaultRunlevel() {
  772.     return default_runlevel;
  773. }
  774.  
  775. global integer SetDefaultRunlevel(string rl) {
  776.     default_runlevel = rl;
  777.     return 0;
  778. }
  779.  
  780. global define list<string> GetAvailableRunlevels() {
  781.     return runlevels;
  782. }
  783.  
  784. /*** LiMaL API PART II. - Files and/or Symlinks in /etc/init.d and /etc/rc?.d ***/
  785. global boolean ServiceAvailable(string service_name)
  786. {
  787.     return (boolean)SCR::Read(.init.scripts.exists, service_name);
  788. }
  789.  
  790. global list<string> GetAvailableServices(boolean simple)
  791. {
  792.     list<string> s = (list<string>)maplist(string service_name, map opts, RunlevelEd::services, {return service_name;});
  793.     if (simple) {
  794.     s = filter(string service_name, s, {return !contains(services[service_name,"defstart"]:[], "B");});
  795.     }
  796.     return s;
  797. }
  798.  
  799. global list<string> GetServiceCurrentStartRunlevels(string service_name)
  800. {
  801.     return services[service_name,"start"]:[];
  802. }
  803.  
  804. global list<string> GetServiceCurrentStopRunlevels(string service_name)
  805. {
  806.     return services[service_name,"stop"]:[];
  807. }
  808.  
  809. /*** LiMaL API PART III. - LSB Comments in Init Scripts ***/
  810.  
  811. //string GetServiceLSBCommentValue(string service_name, string key); // return particular LSB comment
  812. //list_of_strings GetServiceLSBCommentKeys(string service_name); // return list of presented LSB comment keys
  813.  
  814. global list<string> GetServiceDefaultStartRunlevels(string service_name)
  815. {
  816.     return services[service_name,"defstart"]:[];
  817. }
  818.  
  819. global list<string> GetServiceDefaultStopRunlevels(string service_name)
  820. {
  821.     return services[service_name,"defstop"]:[];
  822. }
  823.  
  824. //boolean GetServiceDefaultEnabled(string service_name); // not in LSB?
  825.  
  826. global string GetServiceShortDescription(string service_name)
  827. {
  828.     return services[service_name, "shortdescription"]:"";
  829. }
  830.  
  831. global string GetServiceDescription(string service_name)
  832. {
  833.     return services[service_name, "description"]:"";
  834. }
  835.  
  836. //list_of_strings GetServiceProvides(string service_name);
  837. //list_of_strings GetServiceRequiredStart(string service_name);
  838. //list_of_strings GetServiceRequiredStop(string service_name);
  839. //list_of_strings GetServiceShouldStart(string service_name);
  840. //list_of_strings GetServiceShouldStop(string service_name);
  841.  
  842. /*** LiMaL API PART V. - Installation and Removal of init.d Files ***/
  843.  
  844. /**
  845.  * Enable specified service, and all required services.
  846.  * @param service    service name
  847.  * @param rls        runlevels to enable in or nil for default runlevels
  848.  * @return        0 = ok, 1 = service not found
  849.  */
  850. global integer ServiceInstall(string service, list<string> rls)
  851. {
  852.     if (!haskey(services, service))
  853.     return 1; // service not found
  854.  
  855.     if (rls == nil)
  856.     rls = GetServiceDefaultStartRunlevels(service);
  857.  
  858.     list<string> dep_s = RunlevelEd::ServiceDependencies(service, true);
  859.     dep_s = RunlevelEd::FilterAlreadyDoneServices(dep_s, rls, true, true, false);
  860.     if (dep_s != nil) {
  861.     foreach(string i, dep_s, {
  862.         list<string> default_rls = GetServiceDefaultStartRunlevels(i);
  863.         if (contains(default_rls, "B")) {
  864.         services[i,"start"] = union(["B"], services[service,"start"]:[]);
  865.         } else {
  866.         services[i,"start"] = union(rls, services[service,"start"]:[]);
  867.         }
  868.         services[i,"dirty"] = true;
  869.     });
  870.     }
  871.     services[service,"start"] = union(rls, services[service,"start"]:[]);
  872.     services[service,"dirty"] = true;
  873.     
  874.     return 0;
  875. }
  876.  
  877.  
  878. /**
  879.  * Disable specified service, and all dependence services.
  880.  * @param service    service name
  881.  * @param rls        runlevels to disable in or nil for default runlevels
  882.  * @return        0 = ok
  883.  */
  884. global integer ServiceRemove(string service, list<string> rls)
  885. {
  886.     if (!haskey(services, service))
  887.     return 0; // service not found (no error)
  888.  
  889.     if (rls == nil) {
  890.     rls = GetServiceDefaultStartRunlevels(service);
  891.     }
  892.     list<string> dep_s = RunlevelEd::ServiceDependencies(service, false);
  893.     foreach(string rl, (list<string>)union(rls, ["B"]), {
  894.     list<string> dep_s1 = RunlevelEd::FilterAlreadyDoneServices(dep_s, [rl], false, true, false);
  895.     if (dep_s1 != nil) {
  896.         foreach(string j, dep_s, {
  897.         services[j,"start"] = filter(string i, services[j,"start"]:[], {return !contains(rls, i);});
  898.         services[j,"dirty"] = true;
  899.         });
  900.     }
  901.     });
  902.     services[service,"start"] = filter(string i, services[service,"start"]:[], {return !contains(rls, i);});
  903.     services[service,"dirty"] = true;
  904.     
  905.     return 0;
  906. }
  907.  
  908.     /**
  909.      * Returns items for default runlevel combo box.
  910.      * (Excludes 0, 1, 6, S and B)
  911.      * @param mode if `auto, adds Unchanged. if `proposal, only 2, 3 and 5
  912.      * @return list List of items. Default is selected.
  913.      */
  914.     global list getDefaultPicker (symbol mode) ``{
  915.         list items = [];
  916.         list<string> rls = RunlevelEd::runlevels;
  917.         if (mode == `auto)
  918.         {
  919.             rls = prepend (rls, "");
  920.         }
  921.         else if (mode == `proposal)
  922.         {
  923.             // We could read the list from SCR (#37071) but
  924.             // inittab in the inst-sys is pre-lsb so we have to override it
  925.             rls = ["2", "3", "5"];
  926.         }
  927.  
  928.         foreach (string i, rls, ``{
  929.             // which ones to avoid: #36110
  930.             if ("0" != i && "1" != i && "6" != i && "S" != i && "B" != i)
  931.             {
  932.                 items[size(items)] = `item (`id (i),
  933.                                            RunlevelEd::getRunlevelDescr (i),
  934.                                            i == RunlevelEd::default_runlevel);
  935.             }
  936.         });
  937.         return items;
  938.     }
  939.  
  940.  
  941. }
  942.