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
/
LogView.ycp
< prev
next >
Wrap
Text File
|
2006-11-29
|
21KB
|
747 lines
/**
* File: modules/LogView.ycp
* Package: YaST2
* Summary: Displaying a log with additional functionality
* Authors: Jiri Srain <jsrain@suse.cz>
*
* $Id: LogView.ycp 18863 2004-08-27 13:14:34Z arvin $
*
* All of these functions watch the log file and display
* added lines as the log grows.
* <pre>
* LogView::DisplaySimple ("/var/log/messages");
* LogView::DisplayFiltered ("/var/log/messages", "\\(tftp\\|TFTP\\)");
* LogView::Display ($[
* "file" : "/var/log/messages",
* "grep" : "dhcpd",
* "save" : true,
* "actions" : [ // menu buttons
* [ _("Restart DHCP Server"),
* RestartDhcpDaemon ],
* [ _("Save Settings and Restart DHCP Server"),
* DhcpServer::Write ],
* ],
* ]);
* </pre>
*/
{
module "LogView";
textdomain "base";
import "CWM";
import "Popup";
import "Label";
import "Report";
// fallback settings variables
/**
* default value of maximum displayed lines
*/
integer max_lines_default = 100;
// configuration variables
/**
* global parameters for the log displaying widget
*/
map<string,any> param = $[];
/**
* list of all the logs that can be displayed
*/
list<map<string,any> > logs = [];
/**
* index of currently selected log
*/
integer current_index = 0;
/**
* list of actions that can be processed on the logs
*/
list<list> mb_actions = [];
// status variables
/**
* current lines of the selected log
*/
list<string> lines = [];
// local functions
/**
* Get the map describing the particular log file from its index
* @param index integer index of the log file
* @return a map describing the log file
*/
define map<string,any> Index2Descr (integer index) {
return logs[index]:$[];
}
/**
* Get maximum lines to display for a log
* @param log_descr a map describing the log
* @return integer maximum log lines to display
*/
define integer GetMaxLines (map<string,any> log_descr) {
return log_descr["max_lines"]:param["max_lines"]:max_lines_default;
}
/**
* Starts the log reading command via background agent
* @param index integer the index of the log file
*/
define void InitLogReading (integer index) {
SCR::Execute (.background.kill);
map<string, any> log_descr = Index2Descr (index);
integer max_lines = GetMaxLines (log_descr);
string command = (string) (log_descr["command"]:nil);
if (command == nil || command == "")
{
string file = log_descr["file"]:"";
if (file == nil || file == "")
{
// error report
Report::Error (_("Error occurred while reading the log."));
return;
}
string grep = log_descr["grep"]:"";
if (grep != "" && grep != nil)
grep = sformat ("| grep --line-buffered '%1'", grep);
string lc_command
= sformat ("cat %1 %2 | wc -l", log_descr["file"]:"", grep);
map bash_output = (map)SCR::Execute (.target.bash_output, lc_command);
command = "tail -f -n +0 " + log_descr["file"]:"";
string addon = "";
if (bash_output["exit"]:1 == 0)
{
string lc = bash_output["stdout"]:"";
lc = filterchars (lc, "1234567890");
integer lines_count = tointeger (lc);
lines_count = lines_count - 2 * max_lines;
// don't know why without
// doubling it discards more lines, out of YaST2
// it works
if (max_lines != 0 && lines_count > 0)
addon = sformat ("| tail -n +%1", lines_count);
}
command = sformat ("%1 %2 %3", command, grep, addon);
}
y2milestone ("Calling background agent with command %1", command);
boolean cmdret = (boolean)SCR::Execute (.background.run_output, command);
if (! cmdret)
{
// error report
Report::Error (_("Error occurred while reading the log."));
return;
}
}
/**
* Kill processes running on the backgrouns
* @param key log widget key
*/
define void KillBackgroundProcess (string key) {
SCR::Execute (.background.kill);
}
/**
* Remove unneeded items from a list
* If max_lines is 0, then don't remove anything
* @param lines a list of strings representing log lines
* @param max_lines integer lines that should be saved
* @return a list last max_lines of lines
* FIXME probably used in multiple locations!!!
* FIXME variables are global, no need to have them as parameters
*/
define list<string> DeleteOldLines (list<string> lines, integer max_lines) {
if (0 == max_lines)
return lines;
integer sl = size (lines);
if (sl > max_lines)
{
lines = filter (string l, lines, {
sl = sl -1;
return sl < max_lines;
});
}
return lines;
}
/**
* Fills the log widget with initial data got from the background agent
* @param index integer index of the log file
*/
define void FillWidgetWithData (integer index) {
sleep (100);
map<string,any> log = Index2Descr (index);
integer max_lines = GetMaxLines (log);
integer count = (integer)SCR::Read (.background.newlines);
if (count > 0)
{
lines = (list<string>) SCR::Read (.background.newout);
lines = DeleteOldLines (lines, max_lines);
UI::ChangeWidget (`id (`_cwm_log), `Value,
mergestring (lines, "\n") + "\n");
}
}
/**
* Get the help for the log in case of multiple logs
* @return string part of the log
*/
global define string LogSelectionHelp () {
// help for the log widget, part 1, alt. 1
return _("<p><b><big>Displayed Log</big></b><br>
Use <b>Log</b> to select the log to display. It will be displayed in
the field below.</p>
");
}
/**
* Get the help for the log in case of a single log
* @return string part of the log
*/
global define string SingleLogHelp () {
// help for the log widget, part 1, alt. 2
return _("<p><b><big>The Log</big></b><br>
This screen displays the log.</p>");
}
/**
* Get the second part of the help for the log in case of advanced functions
* and save support
* @param label tge label of the menu button
* @return string part of the log
*/
global define string AdvancedSaveHelp (string label) {
// help for the log widget, part 2, alt. 1, %1 is a menu button label
return sformat (_("<p>
To process advanced actions or save the log into a file, click <b>%1</b>
and select the action to process.</p>"), label);
}
/**
* Get the second part of the help for the log in case of advanced functions
* @param label tge label of the menu button
* @return string part of the log
*/
global define string AdvancedHelp (string label) {
// help for the log widget, part 2, alt. 2, %1 is a menu button label
return sformat (_("<p>
To process advanced actions, click <b>%1</b>
and select the action to process.</p>"), label);
}
/**
* Get the second part of the help for the log in case of save support
* @return string part of the log
*/
global define string SaveHelp () {
// help for the log widget, part 2, alt. 3
return _("<p>
To save the log into a file, click <b>Save Log</b> and select the file
to which to save the log.</p>
");
}
/**
* Get the help of the widget
* @param logs integer count of displayed logs
* @param parameters map parameters of the log to display
* @return string help to the widget
*/
define string CreateHelp (integer logs,
map parameters)
{
string help = parameters["help"]:"";
if (help != "" && help != nil)
return help;
string adv_button = parameters["mb_label"]:"";
if (adv_button == "" || adv_button == nil)
{
// menu button
adv_button = _("Ad&vanced");
}
if (regexpmatch (adv_button, "^.*&.*$"))
adv_button = regexpsub (adv_button, "^(.*)&(.*)$", "\\1\\2");
boolean save = parameters["save"]:false;
if (save == nil)
save = false;
list<map<string,any> > actions_lst = parameters["actions"]:[];
if (actions_lst == nil)
actions_lst = [];
integer actions = size (actions_lst);
if (save)
actions = actions + 1;
if (logs > 1)
{
help = LogSelectionHelp ();
}
else if (actions >= 1 || save)
{
help = SingleLogHelp ();
}
else
{
return "";
}
if (actions >= 2)
{
if (save)
{
help = help + AdvancedSaveHelp (adv_button);
}
else
{
help = help + AdvancedHelp (adv_button);
}
}
else if (save)
{
help = help + SaveHelp ();
}
return help;
}
/**
* Get the combo box of the available log files
* @param log_maps a list of maps describing all the logs
* @return term the combo box widget
*/
define term GetLogSelectionCombo (list<map<string,any> > log_maps) {
term selection_combo = `Empty ();
if (size (log_maps) > 0)
{
integer index = -1;
list items = maplist (map m, log_maps, {
index = index + 1;
return `item (`id (index),
// combo box entry (only used as fallback in case
// of error in the YaST code)
m["log_label"]:m["command"]:m["file"]:_("Log"));
});
selection_combo = `ComboBox (`id (`cwm_log_files),
`opt (`notify, `hstretch),
_("&Log"),
items);
}
return selection_combo;
}
/**
* Get the widget with the menu button with actions to be processed on the log
* @param actions a list of all actions
* @param save boolean true if the log should be offered to be saved
* @param mb_label label of the menu button, may be empty for default
* @return term widget with the menu button
*/
define term GetMenuButtonWidget (list<list> actions, boolean save,
string mb_label)
{
list<any> menubutton = [];
if (save)
// menubutton entry
menubutton = add (menubutton, [`_cwm_log_save, _("&Save Log")]);
if (size (actions) > 0)
{
integer index = 0;
foreach (list a, actions, {
menubutton = add (menubutton, [index, a[0]:""]);
index = index + 1;
});
}
if (size (menubutton) > 1)
{
menubutton = filter (any m, menubutton, ``(
is(m,list) && (((list)m)[0]:nil != nil)
));
menubutton = maplist (any m, menubutton, {
list ml = (list)m;
return `item (`id (ml[0]:nil), ml[1]:"");
});
if (mb_label == "" || mb_label == nil)
{
mb_label = _("Ad&vanced");
}
return `MenuButton (`id (`_cwm_log_menu), mb_label, menubutton);
}
else if (size (menubutton) == 1)
{
return `PushButton (`id (menubutton[0,0]:(any)""), menubutton[0,1]:"");
}
return `Empty ();
}
/**
* Get the buttons below the box with the log
* @param popup boolean true if running in popup (and Close is needed)
* @param glob_param a map of global parameters of the log widget
* @param log_maps a list of maps describing all the logs
* @return term the widget with buttons
*/
define term GetButtonsBelowLog (boolean popup, map<string,any> glob_param,
list<map<string,any> > log_maps)
{
term left = `Empty ();
term center = `Empty ();
term right = `Empty ();
if (popup)
{
center = `PushButton (`id(`close), `opt (`key_F9),
Label::CloseButton ());
if (haskey (glob_param, "help") && glob_param["help"]:"" != "")
{
left = `PushButton (`id (`help), Label::HelpButton ());
}
}
boolean save = glob_param["save"]:false;
string mb_label = glob_param["mb_label"]:_("Ad&vanced");
list<list> actions = glob_param["actions"]:[];
right = GetMenuButtonWidget (actions, save, mb_label);
return `HBox (
`HWeight (1, left),
`HStretch (),
`HWeight (1, center),
`HStretch (),
`HWeight (1, right)
);
}
/**
* Get the default entry for the combo box with logs
* @param log_maps a list of maps describing all the logs
* @return integer the index of the default entry in the combo box
*/
define integer GetDefaultItemForLogsCombo (list<map<string,any> > log_maps) {
integer default_log = 0;
if (size (log_maps) > 0)
{
integer index = -1;
foreach (map m, log_maps, {
index = index + 1;
if (haskey (m, "default") && default_log == 0)
default_log = index;
});
}
return default_log;
}
/**
* Switch the displayed log
* @param index integer index of the log to display
*/
define void LogSwitch (integer index) {
lines = [];
current_index = index;
map<string, any> log_descr = Index2Descr (index);
// logview caption
string caption = log_descr["log_label"]:param["log_label"]:_("&Log");
integer max_lines = GetMaxLines (log_descr);
UI::ReplaceWidget (`_cwm_log_rp,
`LogView (`id (`_cwm_log), caption, 15, max_lines));
InitLogReading (index);
FillWidgetWithData (index);
}
/**
* Initialize the displayed log
* @param key log widget key
* @param key table widget key
*/
global define void LogInit (string key) {
param = CWM::GetProcessedWidget ();
current_index = param["_cwm_default_index"]:0;
mb_actions = param["_cwm_button_actions"]:[];
logs = param["_cwm_log_files"]:[];
if (UI::WidgetExists (`id (`cwm_log_files)))
UI::ChangeWidget (`id (`cwm_log_files), `value, current_index);
LogSwitch (current_index);
}
/**
* Handle the event on the log view widget
* @param key log widget key
* @param event map event to handle
* @return symbol always nil
*/
global define symbol LogHandle (string key, map event) {
param = CWM::GetProcessedWidget ();
map<string,any> log = Index2Descr (current_index);
integer max_lines = GetMaxLines (log);
integer count = (integer)SCR::Read (.background.newlines);
if (count > 0)
{
list<string> new_lines = (list<string>)
SCR::Read (.background.newout);
foreach (string l, new_lines, {
UI::ChangeWidget (`id (`_cwm_log), `LastLine, l + "\n");
});
lines = (list<string>) merge (lines, new_lines);
lines = DeleteOldLines (lines, max_lines);
}
any ret = event["ID"]:nil;
// save the displayed log to file
if (ret == `_cwm_log_save)
{
string filename = UI::AskForSaveFileName(
// popup caption
"/tmp", "*.log", _("Save Log as..."));
if (filename != nil)
{
SCR::Write (.target.string, filename,
mergestring (lines, "\n") + "\n");
}
}
// other operation specified by user
else if (ret != nil && is (ret, integer))
{
integer iret = (integer)ret;
void() func = (void())(mb_actions[iret, 1]:nil);
if (func != nil)
func ();
if (mb_actions[iret, 2]:nil == true)
{
SCR::Execute (.background.kill);
UI::ChangeWidget (`id (`_cwm_log), `Value, "");
InitLogReading (current_index);
}
}
// switch displayed log file
else if (ret == `cwm_log_files)
{
integer index = (integer)
UI::QueryWidget (`id (`cwm_log_files), `Value);
LogSwitch (index);
}
return nil;
}
/**
* Get the map with the log view widget
* @param parameters map parameters of the widget to be created, will be
* unioned with the generated map
* <pre>
* - "save" -- boolean, if true, then log saving is possible
* - "actions" -- list, allows to specify additional actions.
* Each member is a 2- or 3-entry list, first entry is a
* label for the menubutton, the second one is a function
* that will be called when the entry is selected,
* the signature of the function must be void(),
* optional 3rd argument, if set to true, forces
* restarting of the log displaying command after the
* action is performed
* - "mb_label" -- string, label of the menubutton, if not specified,
* then "Advanced" is used
* - "max_lines" -- integer, maximum of lines to be displayed. If 0,
* then display whole file. Default is 100.
* - "help" -- string for a rich text, help to be offered via a popup
* when user clicks the "Help" button. If not present,
* default help is shown or Help button is hidden.
* - "widget_height" -- height of the LogView widget, to be adjusted
* so that the widget fits into the dialog well.
* Test it to find the best value, 15 seems to be
* good value (is default if not specified)
* </pre>
* @param log_files a list of logs that will be displayed
* <pre>
* - "file" -- string, filename with the log
* - "grep" -- string, basic regular expression to be grepped
* in the log (for getting relevant parts of
* /var/log/messages. If empty or not present, whole file
* is used
* - "command" -- allows to specify comand to get the log for cases
* where grep isn't enough. If used, file and grep entries
* are ignored
* - "log_label" -- header of the LogView widget, if not set, then the file
* name or the command is used
* - "default" -- define and set to true to make this log be active after
* widget is displayed. If not defiend for any log, the
* first log is automatically default. If defined for multiple
* logs, the first one is active
* </pre>
* @return map the log widget
*/
global define map CreateWidget (map<string,any> parameters,
list<map<string,any> > log_files)
{
// logview caption
string caption = param["log_label"]:_("&Log");
integer max_lines = param["max_lines"]:max_lines_default;
integer height = param["widget_height"]:15;
integer default_index = GetDefaultItemForLogsCombo (log_files);
term top_bar = GetLogSelectionCombo (log_files);
term bottom_bar = GetButtonsBelowLog (false, parameters, log_files);
return union (
$[
"widget" : `custom,
"custom_widget" : `VBox (
top_bar,
`ReplacePoint (`id (`_cwm_log_rp),
`LogView (`id (`_cwm_log), caption, height, max_lines)
),
bottom_bar
),
"init" : LogInit,
"handle" : LogHandle,
"cleanup" : KillBackgroundProcess,
"ui_timeout" : 1000,
"_cwm_default_index" : default_index,
"_cwm_log_files" : log_files,
"_cwm_button_actions" : [],
"help" : CreateHelp (size (log_files), parameters),
],
parameters
);
}
// old functions for displaying log as a popup
/**
* Main function for displaying logs
* @param parameters map description of parameters, with following keys
* <pre>
* - "file" -- string, filename with the log
* - "grep" -- string, basic regular expression to be grepped
* in the log (for getting relevant parts of
* /var/log/messages. If empty or not present, whole file
* is used
* - "command" -- allows to specify comand to get the log for cases
* where grep isn't enough. If used, file and grep entries
* are ignored
* - "save" -- boolean, if true, then log saving is possible
* - "actions" -- list, allows to specify additional actions.
* Each member is a 2- or 3-entry list, first entry is a
* label for the menubutton, the second one is a function
* that will be called when the entry is selected,
* the signature of the function must be void(),
* optional 3rd argument, if set to true, forces
* restarting of the log displaying command after the
* action is performed
* - "help" -- string for a rich text, help to be offered via a popup
* when user clicks the "Help" button. If not present,
* Help button isn't shown
* - "mb_label" -- string, label of the menubutton, if not specified,
* then "Advanced" is used
* - "max_lines" -- integer, maximum of lines to be displayed. If 0,
* then display whole file. Default is 100.
* - "log_label" -- header of the LogView widget, if not set, then "Log"
* is used
* </pre>
*/
global define void Display (map<string, any> parameters) {
param = parameters;
// menubutton
string mb_label = param["mb_label"]:_("Ad&vanced");
integer max_lines = param["max_lines"]:max_lines_default;
string log_label = param["log_label"]:_("&Log");
logs = [param];
InitLogReading (0);
term button_line = GetButtonsBelowLog (true, param, [param]);
UI::OpenDialog (`HBox (`HSpacing (1), `VBox (
`VSpacing (1),
`HSpacing (70),
// log view header
`LogView (`id (`_cwm_log), log_label, 19, max_lines),
`VSpacing (1),
button_line,
`VSpacing (1)), `HSpacing (1)));
if (param["help"]:"" != "")
{
UI::ReplaceWidget (`id (`rep_left),
`PushButton (`id (`help), Label::HelpButton ()));
}
mb_actions = param["actions"]:[];
FillWidgetWithData (0);
any ret = nil;
while (ret != `close && ret != `cancel)
{
map event = (map)UI::WaitForEvent (1000);
ret = event["ID"]:nil;
if (ret == `help)
{
UI::OpenDialog (`VBox (
`RichText (`id (`help_text), param["help"]:""),
`HBox (
`HStretch (),
`PushButton (`id (`close), Label::CloseButton ()),
`HStretch ()
)
));
while (ret != `close && ret != `cancel)
ret = UI::UserInput ();
ret = nil;
UI::CloseDialog ();
}
else
{
LogHandle ("", event);
}
}
SCR::Execute (.background.kill);
UI::CloseDialog ();
return;
}
/**
* Display specified file, list 100 lines
* @param file string filename of file with the log
*/
global define void DisplaySimple (string file) {
Display ($[ "file" : file ]);
}
/**
* Display log with filtering with 100 lines
* @param file string filename of file with the log
* @param grep string basic regular expression to be grepped in file
*/
global define void DisplayFiltered (string file, string grep) {
Display ($[ "file" : file, "grep" : grep ]);
}
}