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 >
Wrap
Text File
|
2006-11-29
|
51KB
|
1,774 lines
/**
* File:
* ui.ycp
*
* Module:
* System Services (Runlevel) (formerly known as Runlevel Editor)
*
* Summary:
* Runlevel Editor user interface.
*
* Authors:
* Martin Vidner <mvidner@suse.cz>
* Petr Blahos <pblahos@suse.cz>
* Martin Lazar <mlazar@suse.cz>
*
* $Id: ui.ycp 33299 2006-10-10 09:39:23Z locilka $
*
* Runlevel editor user interface functions.
*/
{
textdomain "runlevel";
import "Service";
import "RunlevelEd";
import "Wizard";
import "Label";
import "Popup";
import "FileUtils";
define string StartedText (integer started);
define string BstatusText (boolean disabled, integer started);
define list mapkeys (map m);
define string formatLine (list<string> l, integer len);
define list startStopService (string service_name, string command);
define boolean LongContinueCancelHeadlinePopup (string headline, term richtext, integer hdim, integer vdim);
define void setServiceToDefault (string service_name);
define map<string, boolean> tomap_true (list<string> l);
define void updateServiceStatus (boolean use_func, string service_name);
string current_service = "";
boolean do_abort_now = false;
// We read service status when dialog with services is shown.
// We read status for services taken from list of services (service_list)
// and then update map services.
integer fetching_service_index = 0;
// when fetching_service_status becomes false, we stop fetching services
boolean fetching_service_status = true;
// columns in the table where these items are located
// -1 means it is not there
integer c_dirty = -1;
integer c_bstatus = -1;
integer c_rstatus = -1;
integer c_runlevels = -1;
integer c_descr = -1;
// index into table column for each runlevel
map runlevel2tableindex = $[];
// map of services listed in table
map <string, boolean> service_ids_listed_in_table = $[];
/**
* Create term of checkboxes for runlevel selection.
* @return HBox full of checkboxes.
*/
define term getRlCheckBoxes () ``{
term rls = `HBox (`opt (`hstretch));
foreach (string i, RunlevelEd::runlevels, ``{
if (size (rls) > 1)
rls = add (rls, `HStretch ());
rls = add (rls, `CheckBox (`id (i), `opt (`notify), "&" + i));
});
return rls;
}
/**
* Changes value of a runlevel checkbox.
* Prevents triggering userinput by disabling notify.
* @param service @ref service
* @param rl which runlevel
*/
define void updateRlCheckBox (map service, string rl) ``{
list start = service["start"]:[];
boolean old_notify = (boolean) UI::QueryWidget (`id (rl), `Notify);
UI::ChangeWidget (`id (rl), `Notify, false);
UI::ChangeWidget (`id (rl), `Value, contains (start, rl));
UI::ChangeWidget (`id (rl), `Notify, old_notify);
}
/**
* Changes values of runlevel checkboxes.
* @param service @ref service
*/
define void updateRlCheckBoxes (map service) ``{
foreach (string i, RunlevelEd::runlevels, ``{
updateRlCheckBox (service, i);
});
}
/**
* Update the long description text box
* @param service @ref service
*/
define void updateDescription (map service) ``{
// For the box, use the long description.
// The short one as a fallback. #20853
string descr = service["description"]:"";
if (descr == "")
{
descr = service["shortdescription"]:"";
}
UI::ChangeWidget (`id (`description), `Value, descr);
}
/**
* Sets runlevel columns in the table.
* @param service_name which line
* @param service @ref service
* @param rls which columns to update, nil == all
*/
define void updateRlColumns (string service_name, map service,
list<string> rls) ``{
if (c_runlevels == -1)
{
return;
}
list start = service["start"]:[];
if (rls == nil)
{
rls = RunlevelEd::runlevels;
}
foreach (string rl, rls, ``{
UI::ChangeWidget (`id (`table),
`Item (service_name, runlevel2tableindex[rl]:-1),
contains (start, rl) ? rl : " ");
});
}
/**
* Returns whether the service is listed in the table of services
*
* @param string service_name
*/
define boolean ServiceListedInTable (string service_name) {
return service_ids_listed_in_table[service_name]:false;
}
/**
* Update run-time status column.
* @param service_name which line
* @param started status or -1 (unknown yet)
*/
define void updateStatusColumn (string service_name, integer started) ``{
if (c_rstatus >= 0) {
// cannot change item which is not listed in the table
if (ServiceListedInTable(service_name))
UI::ChangeWidget(`id(`table), `Item(service_name, c_rstatus), StartedText(started));
}
if (c_bstatus >= 0) {
// cannot change item which is not listed in the table
if (ServiceListedInTable(service_name)) {
boolean disabled = RunlevelEd::isDisabled(RunlevelEd::services[service_name]:$[]);
UI::ChangeWidget(`id(`table), `Item(service_name, c_bstatus), BstatusText(disabled, started));
}
}
}
/**
* Helper function for fetching service status in run-time.
* @param service_name which line
* @param service @ref service
* @param started status or -1 (unknown yet)
*/
define void updateStatusInTable (string service_name, map service,
integer started) ``{
// just translate the arguments. the callback is generic
// because of the future simple UI, bug #13789
updateStatusColumn (service_name, started);
}
/**
* Changes values of runlevel checkboxes.
* Get the status if not known yet.
*/
define void changeService1 (map service) ``{
if (service["started"]:-1 < 0)
{
integer started = Service::RunInitScriptWithTimeOut (current_service,
"status" + sformat(" 2>&1 1>%1/runlevel_out", SCR::Read(.target.tmpdir)));
service["started"] = started;
RunlevelEd::services[current_service] = service;
updateStatusColumn (current_service, started);
}
updateRlCheckBoxes (service);
updateDescription (service);
}
/**
* Reads data from checkboxes and updates service
* and RunlevelEd::services maps.
* @param service_name which service
* @param service @ref service
* @return @ref service
*/
define map queryRlCheckBoxes (string service_name, map service) ``{
list start_in = [];
foreach (string i, RunlevelEd::runlevels, ``{
if ((boolean) UI::QueryWidget (`id (i), `Value))
{
start_in[size(start_in)] = i;
}
});
if (service["start"]:[] != start_in)
{
service = union (service, $[
"start": start_in,
"dirty": true
]);
RunlevelEd::services[service_name] = service;
}
return service;
}
// mapping numbers to descriptions
/**
* Get help text for rcscript start|stop command exit value.
* @param exit exit value
* @return string help text
*/
define string getActionReturnHelp (integer exit) ``{
map <integer, string> descr =
$[
// Init script non-status-command return codes
// http://www.linuxbase.org/spec/gLSB/gLSB/iniscrptact.html
// status code.
// Changed "Foo bar." to "foo bar", #25082
0: _("success"),
/* 1: handled as default below */
// status code.
2: _("invalid or excess arguments"),
// status code.
3: _("unimplemented feature"),
// status code.
4: _("user had insufficient privileges"),
// status code.
5: _("program is not installed"),
// status code.
6: _("program is not configured"),
// status code.
7: _("program is not running"),
];
// status code.
return descr[exit]:_("unspecified error");
}
/**
* Get help text for rcscript status return value
* according to LSB.
* @param exit exit value
* @return string help text
*/
define string getStatusReturnHelp (integer exit) ``{
map <integer, string> descr =
$[
// Init script "status" command return codes
// http://www.linuxbase.org/spec/gLSB/gLSB/iniscrptact.html
// status code.
// Changed "Foo bar." to "foo bar", #25082
0: _("program is running"),
// status code.
1: _("program is dead and /var/run pid file exists"),
// status code.
2: _("program is dead and /var/lock lock file exists"),
// status code.
3: _("program is stopped"),
// status code.
4: _("program or service status is unknown"),
];
// status code.
return descr[exit]:_("unspecified error");
}
/**
* @param rll a list of runlevels or nil, meaning "all"
* @return "in [these] runlevels" (translated)
*/
define string getInRunlevels (list<string> rll) ``{
if (rll == nil)
{
// translators: substituted into a message like
// "To enable/disable foo IN ALL RUNLEVELS, this must be done..."
// (do not include the trailing comma here)
return _("in all runlevels");
}
else
{
string s = mergestring (rll, ", ");
// translators: substituted into a message like
// "To enable/disable foo IN RUNLEVELS 3, 5, this must be done..."
// (do not include the trailing comma here)
return size(rll)>1 ? sformat(_("in runlevels %1"), s) : sformat(_("in runlevel %1"), s);
}
}
/**
* @param started status or -1 (unknown yet)
* @return "Yes", "No" or "???"
*/
define string StartedText (integer started) ``{
return (0 == started ?
// is the service started?
_("Yes") :
(started > 0 ?
// is the service started?
_("No") :
// is the service started?
// ???: we do not know yet what is the service state
_("???")));
}
/**
* @param disabled status
* @param started status or -1 (unknown yet)
* @return "Yes", "Yes*", "No", "No*" or "???"
*/
define string BstatusText (boolean disabled, integer started) ``{
// TRANSLATORS: Unknown service status, presented in table
string state = _("???");
if (disabled != nil) {
// TRANSLATORS: Unknown service status presented in table
state = (started<0) ? _("???") : (!disabled ?
// TRANSLATORS: Service status presented in table, Enabled: Yes
_("Yes")
:
// TRANSLATORS: Service status presented in table, Enabled: No
_("No")
);
if (started>=0 && (started>0 != disabled)) {
state = state + "*";
}
}
return state;
}
//start unsorted
/**
* Ask if really abort. Uses boolean changed_settings. Sets boolean do_abort_now.
* @return boolean true if user really wants to abort
*/
define boolean reallyAbort ()``{
if (do_abort_now || !RunlevelEd::isDirty ())
{
do_abort_now = true;
return true;
}
do_abort_now = Popup::ReallyAbort (true);
return do_abort_now;
}
/**
* Create table items from services.
* For simple mode, also filter out critical services: boot ones.
* For Expert mode:
* Mixin: started, start, (short)description.
* @param mix which items to mix in:<pre>
*`simple: id=name, name, bstatus, (short)description
*`complex: id=name, name, rstatus, runlevels, (short)description
*`auto: id=name, name, dirty, runlevels, ?(short)description
*</pre>
* @return list List of items for table.
*/
define list servicesToTable (symbol mix) ``{
boolean m_dirty = mix == `auto;
boolean m_bstatus = mix == `simple;
boolean m_rstatus = mix == `complex;
boolean m_runlevels = mix != `simple;
boolean m_descr = true;
// assume it is not there until placed in
c_dirty = -1;
c_bstatus = -1;
c_rstatus = -1;
c_runlevels = -1;
c_descr = -1;
runlevel2tableindex = $[];
// filter out services that are too important to be messed with
// in the simple mode
map<string,map> services = RunlevelEd::services;
if (mix == `simple)
{
services = (map<string, map>)filter (string s_name, map s, services, ``(
! contains (s["defstart"]:[], "B")
));
}
list items = [];
boolean first = true;
foreach (string k, map v, services, ``{
if (first)
{
first = false;
// preserve current service when switching modes
if (!haskey (services, current_service))
{
current_service = k;
}
}
// id=name, name
term item = `item (`id (k), k);
// column where a item is added
integer col = 1;
if (m_dirty)
{
// dirty
service_ids_listed_in_table[k] = true;
c_dirty = col;
item = add (item, v["dirty"]:false ? UI::Glyph(`CheckMark) : " ");
col = col + 1;
}
if (m_bstatus)
{
// boot status
service_ids_listed_in_table[k] = true;
boolean disabled = RunlevelEd::isDisabled(v);
integer started = v["started"]:-1;
c_bstatus = col;
item = add (item, BstatusText(disabled, started));
col = col + 1;
}
if (m_rstatus)
{
// runtime status
service_ids_listed_in_table[k] = true;
integer started = v["started"]:-1;
c_rstatus = col;
item = add (item, StartedText (started));
col = col + 1;
}
if (m_runlevels)
{
// runlevels
service_ids_listed_in_table[k] = true;
list rl = v["start"]:[];
c_runlevels = col;
foreach (string i, RunlevelEd::runlevels, ``{
runlevel2tableindex[i] = col;
item = add (item, (contains (rl, i)? i : " "));
col = col + 1;
});
}
if (m_descr)
{
// (short)description
// For the table, use the short description.
// The long one as a fallback. #20853
service_ids_listed_in_table[k] = true;
string descr = v["shortdescription"]:"";
if (descr == "")
{
descr = v["description"]:"";
}
c_descr = col;
item = add (item, descr);
col = col + 1;
}
// next
items[size(items)] = item;
});
return items;
}
map <string, boolean> fetched_service_status = $[];
/**
* For each service, determines its status and calls a supplied function.
* @param func function to call
* @see updateStatusInTable
*/
define void serviceStatusIterator (boolean use_func) ``{
if (!fetching_service_status)
{
return;
}
if (fetching_service_index >= size (RunlevelEd::service_list))
{
fetching_service_status = false;
return;
}
string service_name = RunlevelEd::service_list[fetching_service_index]:"";
fetching_service_index = fetching_service_index + 1;
// every switch between `complex and `simple the fetching_service_index is changed to zero
// but only services which were not checked before are checked now
if (fetched_service_status[service_name]:false == false) {
if (ServiceListedInTable(service_name)) {
updateServiceStatus(use_func, service_name);
fetched_service_status[service_name] = true;
}
}
}
/**
* For specified service, determines its status and calls a supplied function.
* @param func function to call
* @see updateStatusInTable
*/
define void updateServiceStatus (boolean use_func, string service_name) ``{
if (RunlevelEd::services[service_name, "started"]:-1 < 0) {
integer started = Service::RunInitScriptWithTimeOut (service_name,
"status" + sformat(" 2>&1 1>%1/runlevel_out", SCR::Read(.target.tmpdir)));
RunlevelEd::services[service_name, "started"] = started;
if (use_func)
updateStatusInTable(service_name, RunlevelEd::services[service_name]:$[], started);
}
}
/**
* help text for progress
* @return help text
*/
define string getHelpProgress () ``{
return
// help text
_("<P><BIG><B>System Service (Runlevel) Initialization</B></BIG><BR>
Please wait...</P>
")
+
// warning
_("<p><b>Note:</b> The system services (runlevel editor) is an expert tool. Only change settings if
you know what you are doing. Otherwise your system might not function properly afterwards.</p>
")
;
}
/**
* Enable or disable a service in some runlevels.
* Set the variables and update the ui (rl columns).
* @param service_name a service
* @param rls which runlevels, nil == disable in all
* @param enable enabling or disabling?
*/
define void SetService (string service_name,
list<string> rls, boolean enable) ``{
map service = RunlevelEd::services[service_name]:$[];
list<string> start_in = nil;
if (rls == nil)
{
start_in = [];
}
else
{
map<string, boolean> start = tomap_true (service["start"]:[]);
foreach (string rl, rls, ``{
start = (map<string, boolean>) add (start, rl, enable);
});
start = (map<string, boolean>) filter (string k, boolean v, start, ``( v == true));
start_in = (list<string>) mapkeys (start);
}
if (service["start"]:[] != start_in)
{
service = union (service, $[
"start": start_in,
"dirty": true
]);
RunlevelEd::services[service_name] = service;
updateRlColumns (service_name, service, rls);
updateStatusColumn (service_name, service["started"]:-1);
}
}
/**
* Check that all the services exist (in RunlevelEd::services).
* If not, popup a list of the missing ones and ask whether
* continue or not. Filter out the missing ones.
* @param services a service list
* @return [continue?, filtered list]
*/
define list CheckMissingServices (list<string> services) ``{
list<string> missing = [];
boolean ok = true;
services = filter (string s, services, ``{
if (haskey (RunlevelEd::services, s))
{
return true;
}
else
{
missing[size(missing)] = s;
return false;
}
});
if (size (missing) > 0)
{
// missing services only occur when enabling
ok = Popup::ContinueCancel (
sformat (
// continue-cancel popup when enabling a service
// %1 is a list of unsatisfied dependencies
_("These required services are missing:\n%1."),
formatLine (missing, 40)));
}
return [ok, services];
}
/**
* Generic function to handle enabling, disabling, starting and
* stoping services and their dependencies, in various runlevels.
* Piece of cake ;-) <br>
* Either of init_time or run_time can be specified (for complex
* mode) or both (for simple mode).
* rls: ignored for -init +run
* What it does: gets dependent services (in the correct order),
* filters ones that are already in the desired state, if there
* are dependencies left, pop up a confirmation dialog, check for
* missing dependencies, perform the action (run-time, then init-time)
* for the deps and the
* service (in this order), displaying output after each error and
* at the end.
*
* @param service_name name of service
* @param rls in which run levels, nil == all
* @param enable on/off
* @param init_time do enable/disable
* @param run_time do start/stop
* @return success (may have been canceled because of dependencies)
*/
define boolean ModifyServiceDep (string service_name,
list<string> rls,
boolean enable,
boolean init_time,
boolean run_time) ``{
y2debug (1, "Modify: %1 %2 %3 %4 %5",
service_name,
rls,
enable,
init_time? "+init": "-init",
run_time? "+run": "-run");
boolean one = (rls != nil) ? (size (rls) == 1) : false;
string command = enable? "start": "stop";
// get dependent services
list<string> dep_s = RunlevelEd::ServiceDependencies (service_name, enable);
y2debug ("DEP: %1", dep_s);
// ensure we have already determined the service status (#36171)
foreach(string service_name, dep_s, {
updateServiceStatus(false, service_name);
});
// filter ones that are ok already
dep_s = RunlevelEd::FilterAlreadyDoneServices (dep_s, rls, enable,
init_time, run_time);
y2debug ("DEP filtered: %1", dep_s);
boolean doit = size (dep_s) == 0;
// if there are dependencies left, pop up a confirmation dialog
if (!doit)
{
string key =
(run_time? "+run": "-run") + "," +
(init_time? "+init": "-init") + "," +
(enable? "on": "off");
map texts = $[
//"-run,-init,..." does not make sense
// *disable*
// continue-cancel popup
// translators: %2 is "in runlevel(s) 3, 5"
// or "in all runlevels"
"-run,+init,off" : _("To disable service %1 %2,
these services must be additionally disabled,
because they depend on it:
%3."),
// *enable*
// continue-cancel popup
// translators: %2 is "in runlevel(s) 3, 5"
// or "in all runlevels"
"-run,+init,on" : _("To enable service %1 %2,
these services must be additionally enabled,
because it depends on them:
%3."),
// *stop*
// continue-cancel popup
"+run,-init,off" : _("To stop service %1,
these services must be additionally stopped,
because they depend on it:
%2."),
// *start*
// continue-cancel popup
"+run,-init,on" : _("To start service %1,
these services must be additionally started,
because it depends on them:
%2."),
// *stop and disable*
// continue-cancel popup
// translators: %2 is "in runlevel(s) 3, 5"
// or "in all runlevels"
"+run,+init,off" :_("To stop service %1 and disable it %2,
these services must be additionally stopped
and disabled, because they depend on it:
%3."),
// *start and enable*
// continue-cancel popup
// translators: %2 is "in runlevel(s) 3, 5"
// or "in all runlevels"
"+run,+init,on" : _("To start service %1 and enable it %2,
these services must be additionally started
and enabled, because it depends on them:
%3."),
];
string dep_formatted = formatLine (dep_s, 40);
doit = Popup::ContinueCancel (
sformat (
texts[key]:"?",
service_name,
// non-init-time does not need the runlevels
// so we omit it not to confuser the translators
init_time? getInRunlevels (rls): dep_formatted,
dep_formatted
)
);
}
// check for missing services
if (doit)
{
list r = CheckMissingServices (dep_s);
doit = r[0]:false;
dep_s = r[1]:[];
}
string rich_message = "";
if (doit)
{
// iterate dep_s, not including service_name
// because we will ask "continue?" on error
// Foreach with a break: find the failing one
find (string s, dep_s, ``{
if (run_time)
{
list ret = startStopService (s, command);
rich_message = rich_message + ret[1]:"";
integer exit = ret[0]:-1;
if (exit != 0)
{
doit = LongContinueCancelHeadlinePopup (
// popup heading
_("An error has occurred."), `RichText (rich_message),
70, 10);
// don't show what we've already seen
rich_message = "";
if (!doit)
{
return true; // break(foreach)
}
}
}
if (init_time)
{
// set the variables and update the ui
SetService (s, rls, enable);
}
return false;
});
}
if (doit)
{
// only for service_name
if (run_time)
{
list ret = startStopService (service_name, command);
rich_message = rich_message + ret[1]:"";
Popup::LongText ("", `RichText(rich_message), 70, 5);
// don't enable the service if it can't be started, #36176
if (ret[0]:-1 != 0) return false;
}
if (init_time)
{
// set the variables and update the ui
SetService (service_name, rls, enable);
}
}
return doit;
}
/**
* Turns a service on or off in the simple mode, ie. resolving
* dependencies and for each service doing start,enable or
* stop,disable.
* @param service_name name of service
* @param rls in which run levels, nil == all
* @return success (may have been canceled because of dependencies)
*/
define boolean SimpleSetServiceDep (string service_name,
boolean enable) ``{
list<string> rls = enable?
RunlevelEd::services[current_service, "defstart"]:[] :
nil;
return ModifyServiceDep (service_name, rls, enable, true, true);
}
/**
* Used for enabling/disabling a service and services depending on
* it in a runlevel or a set of runlevels.
* @param service_name name of service
* @param rls in which run levels, nil == all
* @param enable enable/disable
* @return success (may have been canceled because of dependencies)
*/
define boolean EnableDisableServiceDep (string service_name,
list<string> rls,
boolean enable) ``{
return ModifyServiceDep (service_name, rls, enable, true, false);
}
/**
* Used for starting/stopping a service and services depending on it.
* Displays result popups.
* @param service_name name of service
* @param enable start/stop
* @return success (may have been canceled because of dependencies)
*/
define boolean StartStopServiceDep (string service_name,
boolean enable) ``{
return ModifyServiceDep (service_name, [], enable, false, true);
}
/**
* Starts/stops/checks status of a service
* @param service_name service to start/stop
* @param command "start" or "stop" or "status"
* @return [integer exit_status, string rich_message]
*/
define list startStopService (string service_name,
string command) ``{
UI::OpenDialog (`Label (service_name + " " + command));
y2milestone("%1 -> %2", service_name, command);
string log_filename = sformat("%1/runlevel_out", (string) SCR::Read(.target.tmpdir));
integer cmd_run = Service::RunInitScriptWithTimeOut (
service_name,
command + sformat(" 2>&1 1>%1", log_filename)
);
map out = $[ "exit" : cmd_run ];
if (FileUtils::Exists(log_filename)) {
out["stdout"] = (string) SCR::Read(.target.string, log_filename);
}
UI::CloseDialog ();
integer exit = out["exit"]:-1;
string rich_message = sformat (
// %1 service, %2 command,
// %3 exit status, %4 status description, %5 stdout
// added a colon
_("<p><b>/etc/init.d/%1 %2</b> returned %3 (%4):<br>%5</p>"),
service_name, command, exit,
(command == "status" ?
getStatusReturnHelp (exit) :
getActionReturnHelp (exit)),
sformat ("<pre>%1</pre>", out["stdout"]:"")
);
// succesful stop => status: program not running
integer started = (exit == 0 && command == "stop")? 3: exit;
// normally "started" has the exit code of "status",
// and we may be adding output of a different command
// but it is only tested against zero anyway
map service = RunlevelEd::services[service_name]:$[];
service["started"] = started;
RunlevelEd::services[service_name] = service;
// this won't work in simple mode
// make a map "item"->column_number, depending on mode
// then the functions can consult it and do nothing if not present
updateStatusColumn (service_name, service["started"]:-1);
return [exit, rich_message];
}
/**
* Prints list items into a string, separating them by commas
* and when line exceeds len characters, it does line break (\n).
* It adds 5 spaces before each line.
* Do not expect reasonable results if you set len < 0.
* @param l list of strings
* @param len minimal length of line
* @return string formated string
*/
define string formatLine (list<string> l, integer len) ``{
string s = "";
string line = " ";
string add_sep = "";
string line_sep = "";
foreach (string i, l, ``{
if (size (line) > len)
{
s = s + line_sep + line;
line_sep = ",\n";
line = " ";
}
else
{
line = line + add_sep;
}
line = line + i;
add_sep = ", ";
});
if (size (line) > 0)
{
s = s + line_sep + line;
}
return s;
}
/**
* Checks what services should run in this runlevel and do not run
* or what services run but should not run.
* @return string overview text
*/
define string overviewText () ``{
list<string> should_not_run = [];
list<string> should_run = [];
foreach (string k, map v, RunlevelEd::services, ``{
if (RunlevelEd::StartContainsImplicitly (v["start"]:[],
RunlevelEd::current))
{ // it should run
if (0 != v["started"]:-1)
{
should_run[size(should_run)] = k;
}
}
else
{
if (0 == v["started"]:-1)
{
should_not_run[size(should_not_run)] = k;
}
}
});
string s = "";
if (size (should_run) + size (should_not_run) > 0)
{
// message label
s = s + "\n\n"/* + _("Overview") + "\n\n"*/;
if (size (should_not_run) > 0)
{
// list of services will follow
s = s + _("Following services run in current\nrunlevel although they should not:");
s = s + "\n" + formatLine (should_not_run, 35) + "\n\n";
}
if (size (should_run) > 0)
{
// list of services will follow
s = s + _("Following services do not run in current\nrunlevel although they should:");
s = s + "\n" + formatLine (should_run, 35) + "\n\n";
}
}
return s;
}
/**
* Radio buttons (faking tabs) for switching modes
* @param mode `simple or `complex, which one are we in
* @return RadioButtonGroup term
*/
define term ModeTabs (symbol mode) ``{
term rbg =
// fake tabs using radio buttons
`RadioButtonGroup (
`id (`rbg),
`HBox (
`RadioButton (`id (`simple), `opt (`notify),
// radio button label
_("&Simple Mode"), mode == `simple),
`HSpacing (3),
`RadioButton (`id (`complex), `opt (`notify, `key_F7),
// radio button label
_("&Expert Mode"), mode == `complex)
)
);
return `Left (rbg);
}
/**
* help text services dialog
* @return help text
*/
define string getHelpComplex () ``{
// help text
return
// help text
_("<p>Assign system services to runlevels by selecting the list entry of the respective service then
checking or unchecking the <b>check boxes B-S</b> for the runlevel.</p>
")
+
// help text
_("<p><b>Start/Stop/Refresh:</b> Use this to start or stop services individually.</p>")
+
// help text
_("<P><B>Set and Reset:</B>
Select runlevels in which to run the currently selected service.<ul>
<li><b>Enable the service:</b> Activates the service in the standard runlevels.</li>
<li><b>Disable the service:</b> Deactivates service.</li>
<li><b>Enable all services:</b> Activates all services in their standard runlevels.</li>
</ul></p>
")
+
// The change does not occur immediately. After a reboot the system boots into the specified runlevel.
_("<p>Changes to the <b>default runlevel</b> will take effect next time you boot your computer.</p>")
;
}
/**
* Main dialog for changing services.
* @return symbol for wizard sequencer
*/
define symbol ComplexDialog () ``{
Wizard::SetScreenShotName ("runlevel-2-services");
// currently selected service we are working with
map service = $[];
term header = `header (
// table header
_("Service"),
// table header. is a service running?
_("Running"));
foreach (string i, RunlevelEd::runlevels, ``{
// header = add (header, `Center (" " + i + " "));
header = add (header, `Center (i));
});
// headers in table
header = add (header, _("Description"));
list args = WFM::Args ();
// should we show debugging buttons?
boolean show_debug = args[0]:"" == "debug";
// should we show the Restore to default button?
boolean show_restore = true;
term contents = `VBox (
`VSpacing (0.4),
ModeTabs (`complex),
/*
`HBox (
// preserve 2 spaces at the end.
`Label (_("Current runlevel: ")),
`Label (`opt (`outputField, `hstretch), getRunlevelDescr (RunlevelEd::current))
),
*/
// combo box label
`ComboBox (`id (`default_rl), `opt (`hstretch), _("&Set default runlevel after booting to:"), RunlevelEd::getDefaultPicker (`complex)),
`VSpacing (0.4),
`Table (`id (`table), `opt (`notify, `immediate),
header,
servicesToTable (`complex)
),
`VSquash (
`HBox (
`VSpacing (4.3), // 3+borders in qt, 3 in curses
`RichText (`id (`description), `opt (`shrinkable, `vstretch), "")
)
),
`VBox (
// label above checkboxes
`Label (`id (`service_label), `opt (`hstretch), _("Service will be started in following runlevels:")),
getRlCheckBoxes ()
),
`HBox (
// menubutton label
`MenuButton (_("S&tart/Stop/Refresh"), [
// menu item
`item (`id (`start), _("&Start now ...")),
// menu item
`item (`id (`stop), _("S&top now ...")),
// menu item
`item (`id (`status), _("&Refresh status ...")),
]),
`HStretch (),
`ReplacePoint (
`id (`menubutton),
// menubutton label
`MenuButton (_("Set/&Reset"), [
// menu item
`item (`id (`to_enable), /*`opt (`key_F3),*/ _("&Enable the service")),
// menu item
`item (`id (`to_disable), /*`opt (`key_F5),*/ _("&Disable the service")),
]
+ (!show_restore ? [] :
[
//TODO
])
+
[
// menu item
`item (`id (`to_all_enable), _("Enable &all services")),
]
+ (!show_debug ? [] :
[
// menu item
`item (`id (`depviz), _("Save Dependency &Graph")),
])
)
)
)
);
// dialog caption.
Wizard::SetContents (_("System Services (Runlevel): Details"), contents, getHelpComplex (), true, true);
Wizard::DisableBackButton ();
UI::ChangeWidget (`id (`table), `CurrentItem, current_service);
service = RunlevelEd::services[current_service]:$[];
changeService1 (service);
any ret = nil;
// fetch service which were not checked before
fetching_service_status = true;
fetching_service_index = 0;
while (`next != ret && `back != ret && `abort != ret && `simple != ret)
{
if (ret != nil) y2milestone("RET: %1", ret);
// Kludge, because a `Table still does not have a shortcut.
// #16116
UI::SetFocus (`id (`table));
if (fetching_service_status)
{
ret = UI::PollInput ();
UI::NormalCursor ();
if (nil == ret)
{
serviceStatusIterator (true);
continue;
}
UI::BusyCursor ();
}
else
{
ret = UI::UserInput ();
}
if (ret == `cancel)
{
ret = `abort;
}
current_service = (string) UI::QueryWidget (`id (`table), `CurrentItem);
if (`abort == ret)
{
if (!reallyAbort ())
{
ret = nil;
continue;
}
}
else if (`next == ret)
{
// TODO: check dependencies of all services? (on demand?)
// string nfs_adj = RunlevelEd::CheckPortmap ();
// ...
if (RunlevelEd::isDirty ())
{
// yes-no popup
if (!Popup::YesNo (_("Now the changes to runlevels \nwill be saved.")))
{
ret = nil;
continue;
}
}
RunlevelEd::default_runlevel = (string) UI::QueryWidget (`id (`default_rl), `Value);
break;
}
else if (`table == ret)
{
service = RunlevelEd::services[current_service]:$[];
changeService1 (service);
}
else if (nil != ret && is (ret, string))
{
// checkbox pressed
// - enable/disable current_service in one runlevel
boolean enable = (boolean) UI::QueryWidget (`id (ret), `Value);
list<string> rls = (list<string>) [ret];
if (!EnableDisableServiceDep (current_service, (list<string>) [ret], enable))
{
// restore the check box
updateRlCheckBox (service, (string) ret);
}
}
else if (`depviz == ret)
{
string filename = UI::AskForSaveFileName(".", "*", "");
SCR::Write (.target.string, filename,
RunlevelEd::DotRequires ());
}
else if (`to_enable == ret)
{
list<string> default_runlevel = RunlevelEd::services[current_service, "defstart"]:[];
EnableDisableServiceDep (current_service, default_runlevel, true);
service = RunlevelEd::services[current_service]:$[];
changeService1 (service);
}
else if (`to_disable == ret)
{
EnableDisableServiceDep (current_service, nil, false);
service = RunlevelEd::services[current_service]:$[];
changeService1 (service);
}
else if (`to_all_enable == ret)
{
// yes-no popup
if (Popup::YesNo (_("Really enable all services?")))
{
foreach (string k, map v, RunlevelEd::services, ``{
setServiceToDefault (k);
});
UI::ChangeWidget (`id (`table), `Items, servicesToTable (`complex));
// message popup
Popup::Message (_("Each service was enabled\nin the appropriate runlevels."));
}
}
else if (`start == ret || `stop == ret)
{
boolean really = true;
if (`stop == ret && "xdm" == current_service)
{
// yes-no popup. the user wants to stop xdm
if (!Popup::YesNo ( _("This may kill your X session.\n\nProceed?")))
{
really = false;
}
}
if (really)
{
StartStopServiceDep (current_service, ret == `start);
}
}
else if (`status == ret)
{
// similar to startStopService but there will be changes
// when dependencies are checked
//TODO: find a place for it
//Popup::Message (overviewText ());
list r = startStopService (current_service, "status");
Popup::LongText ("", `RichText(r[1]:""), 70, 5);
}
}
Wizard::RestoreScreenShotName ();
return (symbol) ret;
}
// simple mode
/**
* Main dialog for changing services.
* @return symbol for wizard sequencer
*/
define symbol SimpleDialog () ``{
Wizard::SetScreenShotName ("runlevel-2-simple");
map service = $[];
string help_text =
// Simple mode dialog
// help text
_("<p>Here, specify which system services should be started.</p>")
+
// warning
_("<p><b>Warning:</b> The system services (runlevel editor) is an expert tool. Only change settings if you know
what you are doing. Otherwise your system might not function properly afterwards.</p>
")
+
// help text
// Activate is a button
_("<p><b>Activate</b> starts the selected service and services
that it depends on and enables them to start at system boot time.
Likewise, <b>Deactivate</b> stops services that depend on a given service
and the service itself and disables their start at system boot time.</p>")
+
// help text
_("<p>An asterisk (*) after a service status means that the service is enabled but not running or is disabled but running now.</p>")
+
// help text
_("<p>To change the behavior of runlevels and system services in detail, click <b>Expert Mode</b>.</p>\n")
;
term contents = `VBox (
`VSpacing (0.4),
ModeTabs (`simple),
`VSpacing (0.4),
`Table (`id (`table), `opt (`notify, `immediate),
`header (_("Service"), _("Enabled"), _("Description")),
servicesToTable (`simple)
),
`VSquash (
`HBox (
`VSpacing (4.3), // 3+borders in qt, 3 in curses
`RichText (`id (`description), `opt (`shrinkable, `vstretch), "")
)
),
`HBox (
// Button label
`PushButton (`id (`enable), `opt (`key_F3), _("Ena&ble")),
// Button label
`PushButton (`id (`disable), `opt (`key_F5), _("&Disable"))
)
);
// dialog caption.
Wizard::SetContentsButtons (_("System Services (Runlevel): Services"), contents, help_text, Label::BackButton(), Label::FinishButton());
Wizard::DisableBackButton ();
UI::ChangeWidget (`id (`table), `CurrentItem, current_service);
service = RunlevelEd::services[current_service]:$[];
updateDescription (service);
UI::SetFocus (`id (`table));
any ret = nil;
boolean focustable = false;
// fetch service which were not checked before
fetching_service_status = true;
fetching_service_index = 0;
while (`next != ret && `back != ret && `abort != ret && `complex != ret)
{
if (ret != nil) y2milestone("RET: %1", ret);
if (focustable) {
// Kludge, because a `Table still does not have a shortcut.
// #16116
UI::SetFocus (`id (`table));
}
if (fetching_service_status)
{
ret = UI::PollInput ();
UI::BusyCursor ();
if (nil == ret)
{
serviceStatusIterator (true);
continue ;
}
}
else
{
ret = UI::UserInput ();
focustable = true;
}
if (ret == `cancel)
{
ret = `abort;
}
current_service = (string) UI::QueryWidget (`id (`table), `CurrentItem);
if (`abort == ret)
{
if (!reallyAbort ())
{
ret = nil;
continue;
}
}
else if (`next == ret)
{
// FIXME copied from ComplexDialog, make it a function
// Misaligned for consistency with the original
// TODO: check dependencies of all services? (on demand?)
// string nfs_adj = RunlevelEd::CheckPortmap ();
// ...
if (RunlevelEd::isDirty ())
{
// yes-no popup
if (!Popup::YesNo (_("Now the changes to runlevels \nwill be saved.")))
{
ret = nil;
continue;
}
}
break;
}
else if (`table == ret)
{
service = RunlevelEd::services[current_service]:$[];
updateDescription (service);
}
else if (`disable == ret)
{ //FIXME...
y2milestone ("%1", ret);
SimpleSetServiceDep (current_service, false);
}
else if (`enable == ret)
{ //FIXME...
y2milestone ("%1", ret);
SimpleSetServiceDep (current_service, true);
}
}
Wizard::RestoreScreenShotName ();
return (symbol) ret;
}
// Autoyast UI
/**
* Help text for auto-complex-screen
* @return help text
*/
define string getHelpAuto () ``{
return
// help text
_("<p><b>Prepare data for autoinstallation.</b></p>")
+
// help text
_("<p>Change the services to requested state. Only services marked as changed will really be changed in the target system.</p>")
+
// help text
_("<p>If you made a mistake and want to undo the change, press <b>Clear</b> or <b>Clear all</b>.</p>")
;
}
/**
* Add service by hand.
* @return new service name (already added to RunlevelEd::services) or ""
*/
define string addService () ``{
UI::OpenDialog (`VBox (
// dialog heading
`Heading (`opt (`hstretch), _("Add service")),
`VSpacing (1),
// text entry
`TextEntry (`id (`name), _("Service &name")),
// label
`Label (`opt (`hstretch), _("Starts in these runlevels by default:")),
getRlCheckBoxes (),
`VSpacing (1),
// text entry
`TextEntry (`id (`des), _("&Description (optional)"), ""),
`VSpacing (1),
`HBox (`PushButton (`id (`ok), `opt (`default, `key_F10),
Label::OKButton ()),
`PushButton (`id (`cancel), `opt (`key_F9),
Label::CancelButton ()))
));
UI::SetFocus (`id (`name));
any ret = nil; // (symbol|string)
string name = "";
while (true)
{
ret = UI::UserInput ();
if (`ok == ret)
{
name = (string) UI::QueryWidget (`id (`name), `Value);
if (nil == name || "" == name || haskey (RunlevelEd::services, name))
{
// message popup
Popup::Message (_("Invalid service name. You did not specify service
name or the name specified is already in use."));
continue;
}
list def = [];
foreach (string i, RunlevelEd::runlevels, ``{
if ((boolean) UI::QueryWidget (`id (i), `Value))
{
def[size(def)] = i;
}
});
map m = $[
"dirty" : true,
"defstart" : def,
"start" : def,
"description" : UI::QueryWidget (`id (`des), `Value)
];
RunlevelEd::services[name] = m;
break;
}
if (`cancel == ret)
{
name = "";
break;
}
}
UI::CloseDialog ();
return name;
}
/**
* Main dialog for changing services.
* @return symbol for wizard sequencer
*/
define symbol AutoDialog () ``{
Wizard::SetScreenShotName ("runlevel-2-auto");
// currently selected service we are working with
map service = $[];
/**
* Sets columns 0-S (runlevels) in table so they are synchronized with checkboxes.
*/
define void refreshTableLine2 () ``{
updateRlColumns (current_service, service, nil);
UI::ChangeWidget (`id (`table), `Item (current_service, c_dirty),
service["dirty"]:false ? UI::Glyph(`CheckMark) : " ");
}
// headers in table
term header = `header (
// table header
_("Service"),
// table header. has the service state changed?
_("Changed"));
foreach (string i, RunlevelEd::runlevels, ``{
// header = add (header, `Center (" " + i + " "));
header = add (header, `Center (i));
});
// headers in table
header = add (header, _("Description"));
term contents = `VBox (
`VSpacing (0.4),
// combo box label
`ComboBox (`id (`default_rl), `opt (`hstretch), _("&Set default runlevel after booting to:"), RunlevelEd::getDefaultPicker (`auto)),
`VSpacing (0.4),
`Table (`id (`table), `opt (`notify, `immediate),
header,
servicesToTable (`auto)
),
`VBox (
// label above checkboxed
`Label (`id (`service_label), `opt (`hstretch), _("Service will be started in following runlevels:")),
getRlCheckBoxes ()
),
`HBox (
// button label
`PushButton (`id (`add), `opt (`key_F3), _("A&dd")),
`HStretch (),
// button label
`PushButton (`id (`clear), _("&Clear")),
// button label
`PushButton (`id (`clear_all), _("Clea&r All")),
// button label
`PushButton (`id (`default), _("D&efault"))
)
);
// dialog caption.
Wizard::SetContentsButtons (_("System Services (Runlevel): Details"), contents, getHelpAuto (), Label::BackButton(), Label::FinishButton());
Wizard::DisableBackButton ();
UI::ChangeWidget (`id (`table), `CurrentItem, current_service);
service = RunlevelEd::services[current_service]:$[];
updateRlCheckBoxes (service);
any ret = nil;
while (nil != UI::PollInput ()) {sleep(50);}
while (`next != ret && `back != ret && `abort != ret)
{
// Kludge, because a `Table still does not have a shortcut.
// #16116
UI::SetFocus (`id (`table));
ret = UI::UserInput ();
if (ret == `cancel)
{
ret = `abort;
}
if (`abort == ret)
{
if (!reallyAbort ())
{
ret = nil;
continue;
}
}
else if (`next == ret)
{
//FIXME dependencies none or proper
string nfs_adj = RunlevelEd::CheckPortmap ();
if (nil != nfs_adj)
{
UI::ChangeWidget (`id (`table), `CurrentItem, "portmap");
current_service = "portmap";
service = RunlevelEd::services["portmap"]:$[];
updateRlCheckBoxes (service);
while (nil != UI::PollInput ()) {sleep(50);}
// yes-no popup
if (!Popup::YesNo (sformat (_("Service portmap, which is required by
%1, is disabled. Enable
portmap if you want to run %1.
Leave portmap
disabled?\n"), nfs_adj)))
{
ret = nil;
continue;
}
}
RunlevelEd::default_runlevel = (string) UI::QueryWidget (`id (`default_rl), `Value);
break;
}
else if (`add == ret)
{
string name = addService ();
if ("" != name)
{
UI::ChangeWidget (`id (`table), `Items, servicesToTable (`auto));
UI::ChangeWidget (`id (`table), `CurrentItem, name);
// qt and curses behave differently:
// one of them sends notification after changewidget
// and the other does not.
// So eat it.
while (nil != UI::PollInput ()) {sleep(50);}
ret = `table;
}
}
else if (`default == ret)
{
setServiceToDefault (current_service);
service = RunlevelEd::services[current_service]:$[];
refreshTableLine2 ();
ret = `table;
}
else if (`clear == ret)
{
// re-read from SCR
service = Service::Info (current_service);
RunlevelEd::services[current_service] = service;
refreshTableLine2 ();
ret = `table;
}
else if (`clear_all == ret)
{
RunlevelEd::ClearServices ();
UI::ChangeWidget (`id (`table), `Items, servicesToTable (`auto));
ret = `table;
}
else if (nil != ret && is (ret, string))
{
// checkbox pressed
// checked or unchecked?
string checked = (((boolean) UI::QueryWidget (`id (ret), `Value)) ? (string) ret : " ");
service = queryRlCheckBoxes (current_service, service);
UI::ChangeWidget (`id (`table), `Item (current_service, runlevel2tableindex[ret]:-1), checked);
UI::ChangeWidget (`id (`table), `Item (current_service, c_dirty), UI::Glyph(`CheckMark));
}
// not a part of the else-if chain above!
if (`table == ret)
{
current_service = (string) UI::QueryWidget (`id (`table), `CurrentItem);
service = RunlevelEd::services[current_service]:$[];
updateRlCheckBoxes (service);
while (nil != UI::PollInput ()) {sleep(50);}
}
}
Wizard::RestoreScreenShotName ();
return (symbol) ret;
}
// generic ui helpers
/**
* Like Popup::LongText
* @param headline a headline
* @param richtext `RichText(_("<p>foo...</p>"))
* @param hdim popup width
* @param vdim popup height
* @return continue?
*/
define boolean LongContinueCancelHeadlinePopup (
string headline, term richtext, integer hdim, integer vdim) ``{
UI::OpenDialog (
`opt (`decorated),
`HBox (
`VSpacing (vdim),
`VBox (
`HSpacing (hdim),
`Left (`Heading (headline)),
`VSpacing (0.2),
richtext, // scrolled text
`HBox (
`PushButton (
`id (`continue),
`opt (`default, `key_F10),
Label::ContinueButton ()),
`PushButton (
`id (`cancel),
`opt (`key_F9),
Label::CancelButton ())
)
)
)
);
UI::SetFocus (`id (`continue));
boolean ret = UI::UserInput() == `continue;
UI::CloseDialog();
return ret;
}
// move them to the module
// (no ui interaction)
/**
* Disable the service. Changes global services.
* @param service_name name of the service.
*/
define void setServiceDisable (string service_name) ``{
map service = RunlevelEd::services[service_name]:$[];
RunlevelEd::services[service_name] = union (service,
$[
"start": [],
"dirty": true,
]);
}
/**
* DUH, in fact ENABLES the service.
* but the described function will be there sometime
*
* Set service to its default state upon installation.
* Changes global services.
* @param service_name Name of service to process.
*/
define void setServiceToDefault (string service_name) ``{
map service = RunlevelEd::services[service_name]:$[];
RunlevelEd::services[service_name] = union (service,
$[
"start": service["defstart"]:[],
"dirty": true,
]);
}
// favorite missing builtins
/**
* Converts a list to a map with values of true
* @param l a list
* @return a map
*/
define map<string, boolean> tomap_true (list<string> l) ``{
return (map<string, boolean>) listmap (string i, l, ``( $[i: true] ));
}
/**
* @param m a map
* @return keys of the map
*/
define list mapkeys (map m) ``{
return maplist (any k, any v, m, ``( k ));
}
}