I have fixed bugs in inserting new events, and also added real time rendering option in
source dialog, and will be grateful for feedback.
diff -u -X ex or_src/audit-viewer.glade oav/src/audit-viewer.glade
--- or_src/audit-viewer.glade 2012-09-22 04:04:40.000000000 +0400
+++ oav/src/audit-viewer.glade 2015-03-30 12:13:06.810004999 +0300
@@ -2664,6 +2664,16 @@
</widget>
</child>
<child>
+ <widget class="GtkCheckButton"
id="source_with_updating">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label"
translatable="yes">_Render updates in real time</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ </child>
+ <child>
<widget class="GtkVBox" id="vbox25">
<property name="visible">True</property>
<child>
diff -u -X ex or_src/event_source.py oav/src/event_source.py
--- or_src/event_source.py 2009-06-09 23:10:41.000000000 +0400
+++ oav/src/event_source.py 2015-03-30 12:41:11.028004422 +0300
@@ -19,7 +19,7 @@
import datetime
import os.path
import re
-
+import os
import auparse
__all__ = ('ClientEventSource', 'ClientWithRotatedEventSource',
@@ -95,7 +95,7 @@
'''A source of audit events, reading from an auparse
parser.'''
def read_events(self, filters, wanted_fields, want_other_fields,
- keep_raw_records):
+ keep_raw_records, direction = None, tab = None):
'''Return a sequence of audit events read from parser.
Use filters to select events. Store wanted_fields in event.fields, the
@@ -265,6 +265,107 @@
def _create_parser(self):
return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.str)
+class UpdatableEventSourceReader():
+
+ '''A separate reader of audit events, created for each tab of main
window.
+ To be used with UpdatableEventSource.
+
+ '''
+
+ def __init__(self, event_source, tab):
+ self.event_source = event_source
+ self.tab = tab
+ self.up_file = open(event_source.base)
+ self.bottom_file = open(event_source.base)
+ self.up_file.seek(0, 2)
+ self.up_pos = self.down_pos = self.up_file.tell()
+ self.bytes = self.event_source.avg_line_length * self.event_source.chunk_size
+
+ def read_down(self):
+ ''' read older lines from file starting from
self.down_pos'''
+ if os.path.exists(self.bottom_file.name):
+ if os.stat(self.bottom_file.name).st_ino !=
os.fstat(self.bottom_file.fileno()).st_ino:
+ self.bottom_file.close()
+ self.tab.want_read_down = False
+ return ''
+ if self.down_pos <= self.bytes:
+ self.bottom_file.seek(0)
+ lines = self.bottom_file.read(self.down_pos)
+ try:
+ files = os.listdir(os.path.dirname(self.event_source.base))
+ files = sorted(files, key = lambda x : os.stat(x).st_mtime)
+ filename = self.bottom_file.name
+ self.bottom_file.close()
+ self.bottom_file = open(files[files.index(filename) + 1])
+ self.bottom_file.seek(0, 2)
+ self.down_pos = self.bottom_file.tell()
+ except:
+ self.tab.want_read_down = False
+ else:
+ self.bottom_file.seek(self.down_pos - self.bytes)
+ lines = self.bottom_file.read(self.bytes)
+ lines = lines[lines.find('\n') + 1:]
+ self.down_pos -= len(lines)
+ return lines
+
+ def read_up(self):
+ '''try to read new lines if there are any after the previous
reads'''
+ if os.path.exists(self.up_file.name):
+ if os.stat(self.up_file.name).st_ino !=
os.fstat(self.up_file.fileno()).st_ino:
+ self.up_file.close()
+ self.up_file = open(self.event_source.base)
+ self.up_pos = 0
+ else:
+ self.up_file.close()
+ self.tab.want_read_up = False
+ return ''
+ self.up_file.seek(0, 2)
+ file_end_pos = self.up_file.tell()
+ if self.up_pos != file_end_pos:
+ self.up_file.seek(self.up_pos)
+ if file_end_pos - self.up_pos > self.bytes*5:
+ lines = self.up_file.read(self.bytes)
+ file_end_pos = self.up_file.tell()
+ else:
+ lines = self.up_file.read()
+ if lines.endswith('\n'):
+ self.up_pos = file_end_pos
+ else:
+ lines = lines[:lines.rfind('\n') + 1]
+ self.up_pos += len(lines)
+ return lines
+ return ''
+
+
+class UpdatableEventSource(_ParserEventSource):
+
+ def __init__(self, base_file, chunk_size = 50, avg_line_length = 74):
+ self.chunk_size = chunk_size
+ self.avg_line_length = avg_line_length
+ self.readers = {}
+ self.base = base_file
+ self.current_lines = ''
+
+ def add_tab(self, tab):
+ self.readers[tab] = UpdatableEventSourceReader(self, tab)
+
+ def remove_tab(self, tab):
+ del self.readers[tab]
+
+ def read_events(self, filters, wanted_fields, want_other_fields,
+ keep_raw_records, direction, tab):
+ if direction == 'up':
+ self.current_lines = self.readers[tab].read_up()
+ else:
+ self.current_lines = self.readers[tab].read_down()
+ return _ParserEventSource.read_events(self, filters, wanted_fields,
want_other_fields,
+ keep_raw_records)
+
+
+ def _create_parser(self):
+ return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.current_lines)
+
+
def check_expression(expr):
'''Check expr.
diff -u -X ex or_src/list_tab.py oav/src/list_tab.py
--- or_src/list_tab.py 2009-12-19 10:00:00.000000000 +0300
+++ oav/src/list_tab.py 2015-03-30 13:26:44.579005427 +0300
@@ -28,6 +28,7 @@
from search_entry import SearchEntry
from tab import Tab
import util
+import event_source
__all__ = ('ListTab')
@@ -130,7 +131,7 @@
date_column_label = '__audit_viewer_date'
__list_number = 1
- def __init__(self, filters, main_window, will_refresh = False):
+ def __init__(self, filters, main_window, will_refresh = True):
Tab.__init__(self, filters, main_window, 'list_vbox')
# date_column_label == event date, None == all other columns
@@ -157,6 +158,13 @@
util.connect_and_run(self.selection, 'changed',
self.__selection_changed)
+ if isinstance(self.main_window.event_source, event_source.UpdatableEventSource):
+ self.main_window.event_source.add_tab(self)
+ self.want_read_up = True
+ self.want_read_down = True
+ self.__refresh_dont_read_events = False
+ self.refresh(False, 'up')
+ return
self.__refresh_dont_read_events = will_refresh
self.refresh()
self.__refresh_dont_read_events = False
@@ -191,20 +199,25 @@
% (util.filename_to_utf8(filename),
e.strerror))
- def refresh(self):
- event_sequence = self.__refresh_get_event_sequence()
+ def refresh(self, updating = False, direction = None):
+ if isinstance(self.main_window.event_source, event_source.UpdatableEventSource)
and not updating:
+ self.want_read_down = True
+ self.want_read_up = True
+ self.main_window.event_source.readers[self] =
event_source.UpdatableEventSourceReader(self.main_window.event_source, self)
+ event_sequence = self.__refresh_get_event_sequence(direction, self)
if event_sequence is None:
return
-
- if self.filters:
- t = _(', ').join(f.ui_text() for f in self.filters)
- else:
- t = _('None')
- self.list_filter_label.set_text(t)
- self.__refresh_update_tree_view()
+ if not updating:
+ if self.filters:
+ t = _(', ').join(f.ui_text() for f in self.filters)
+ else:
+ t = _('None')
+ self.list_filter_label.set_text(t)
+ self.__refresh_update_tree_view()
events = self.__refresh_collect_events(event_sequence)
- self.__refresh_update_store(events)
+ self.__refresh_update_store(events, updating)
+
def report_on_view(self):
self.main_window.new_report_tab(self.filters)
@@ -462,7 +475,7 @@
for record in event.records
for (key, value) in record.fields]))
- def __refresh_get_event_sequence(self):
+ def __refresh_get_event_sequence(self, direction = None, tab = None):
'''Return an event sequence (as if from
self.main_window.read_events()).
Return None on error.
@@ -480,7 +493,7 @@
elif title is not self.date_column_label:
wanted_fields.add(title)
return self.main_window.read_events(self.filters, wanted_fields,
- want_other_fields, True)
+ want_other_fields, True, direction, tab)
def __refresh_update_tree_view(self):
'''Update self.list_tree_view for current configuration.
@@ -560,7 +573,32 @@
events.sort(key = lambda event: event[0], reverse = self.sort_reverse)
return events
- def __refresh_update_store(self, events):
+ def __insert_row(self, event):
+ ''' insert new row with event into self.store preserving sort
order'''
+ it = None
+ if not isinstance(self.main_window.event_source,
event_source.UpdatableEventSource):
+ it = self.store.append(event[1])
+ return it
+ if self.sort_by and self.sort_by not in self.columns:
+ self.sort_by = None
+ if self.sort_by:
+ sort_field = self.__field_columns.index(self.sort_by) + 1
+ for i in range(len(self.store)):
+ if event[0] > self.store[i][sort_field] and self.sort_reverse or \
+ event[0] <= self.store[i][sort_field] and not self.sort_reverse:
+ it = self.store.insert(i, event[1])
+ break
+ else:
+ for i in range(len(self.store)):
+ if event[1][0].id.sec > self.store[i][0].id.sec and self.sort_reverse
or \
+ event[1][0].id.sec <= self.store[i][0].id.sec and not
self.sort_reverse:
+ it = self.store.insert(i, event[1])
+ break
+ if not it:
+ it = self.store.append(event[1])
+ return it
+
+ def __refresh_update_store(self, events, updating = False):
'''Update self.store and related data.
events is the result of self.__refresh_collect_events().
@@ -571,11 +609,12 @@
key = pos.event_key
l = positions_for_event_key.setdefault(key, [])
l.append(pos)
- self.store.clear()
+ if not updating:
+ self.store.clear()
if (self.text_filter is None and
len(positions_for_event_key) == 0): # Fast path
for event in events:
- self.store.append(event[1])
+ self.__insert_row(event)
else:
event_to_it = {}
text_filter = self.text_filter
@@ -604,7 +643,8 @@
or (self.__other_column_event_text(event_tuple[0]).
find(self.text_filter) == -1))):
continue
- it = self.store.append(event_tuple)
+
+ it = self.__insert_row(event)
event_id = event_tuple[0].id
key = (event_id.serial, event_id.sec, event_id.milli)
if key in positions_for_event_key:
diff -u -X ex or_src/main.py oav/src/main.py
--- or_src/main.py 2008-06-26 00:17:59.000000000 +0400
+++ oav/src/main.py 2015-03-25 15:31:29.663932132 +0300
@@ -29,6 +29,7 @@
from main_window import MainWindow
import settings
import util
+import event_source
_ = gettext.gettext
@@ -48,12 +49,20 @@
help = _('do not attempt to start the privileged backend '
'for reading system audit logs'))
parser.set_defaults(unprivileged = False)
+ parser.add_option('-p', '--updatable', action =
'store_true',
+ dest = 'updatable',
+ help = _('read new lines from log '))
+ parser.set_defaults(updatable = False)
+ parser.add_option('-s', '--source', type = 'string',
+ dest = 'source',
+ help = _('path to log file '))
(options, args) = parser.parse_args()
gnome.init(settings.gettext_domain, settings.version)
gtk.glade.bindtextdomain(settings.gettext_domain, settings.localedir)
gtk.glade.textdomain(settings.gettext_domain)
+ ev_source = None
if options.unprivileged:
cl = None
else:
@@ -66,7 +75,9 @@
sys.exit(1)
except client.ClientNotAvailableError:
cl = None
+ if options.updatable:
+ ev_source = event_source.UpdatableEventSource(options.source)
- w = MainWindow(cl)
+ w = MainWindow(cl, ev_source)
if w.setup_initial_window(args):
gtk.main()
diff -u -X ex or_src/main_window.py oav/src/main_window.py
--- or_src/main_window.py 2008-08-19 14:38:16.000000000 +0400
+++ oav/src/main_window.py 2015-03-30 13:21:25.607005136 +0300
@@ -135,6 +135,8 @@
'''
try:
+ if isinstance(self.event_source, event_source.UpdatableEventSource):
+ self.updater = gobject.idle_add(self.update_tabs)
if isinstance(self.event_source, event_source.EmptyEventSource):
self.__event_error_report_only_one_push()
if self.client is not None:
@@ -246,7 +248,7 @@
return (filename, extension)
def read_events(self, filters, wanted_fields, want_other_fields,
- keep_raw_records):
+ keep_raw_records, direction = None, tab = None):
'''Read audit events.
Return a sequence of events, or None on error (without throwing
@@ -262,7 +264,7 @@
try:
return self.event_source.read_events(filters, wanted_fields,
want_other_fields,
- keep_raw_records)
+ keep_raw_records, direction, tab)
except IOError, e:
if (self.__event_error_report_only_one_depth == 0 or
not self.__event_error_reported):
@@ -381,16 +383,30 @@
'''End a region in which only one error message should be
reported.'''
self.__event_error_report_only_one_depth -= 1
- def __refresh_all_tabs(self):
+ def update_tabs(self):
+ self.__refresh_all_tabs(True)
+ return True
+
+ def __refresh_all_tabs(self, updating = False):
'''Refresh all tabs, taking care to report errors only
once.'''
self.__event_error_report_only_one_push()
try:
- for page_num in xrange(self.main_notebook.get_n_pages()):
- tab = self.__tab_objects[self.main_notebook
- .get_nth_page(page_num)]
- tab.refresh()
+ if not updating:
+ for page_num in xrange(self.main_notebook.get_n_pages()):
+ tab = self.__tab_objects[self.main_notebook
+ .get_nth_page(page_num)]
+ tab.refresh()
+ else:
+ for page_num in xrange(self.main_notebook.get_n_pages()):
+ tab = self.__tab_objects[self.main_notebook
+ .get_nth_page(page_num)]
+ if tab.want_read_up:
+ tab.refresh(True, 'up')
+ if tab.want_read_down:
+ tab.refresh(True, 'down')
finally:
self.__event_error_report_only_one_pop()
+ return True
def __menu_new_list_activate(self, *_):
self.new_list_tab([])
Only in oav/src/: patch
diff -u -X ex or_src/source_dialog.py oav/src/source_dialog.py
--- or_src/source_dialog.py 2009-06-09 22:35:26.000000000 +0400
+++ oav/src/source_dialog.py 2015-03-30 12:46:15.983005127 +0300
@@ -39,7 +39,8 @@
'source_path', 'source_path_browse',
'source_path_label',
'source_type_file', 'source_type_log',
- 'source_with_rotated')
+ 'source_with_rotated',
+ 'source_with_updating')
def __init__(self, parent, client):
DialogBase.__init__(self, 'source_dialog', parent)
@@ -58,6 +59,8 @@
self.__source_type_log_toggled)
util.connect_and_run(self.source_type_file, 'toggled',
self.__source_type_file_toggled)
+ util.connect_and_run(self.source_with_updating, 'toggled',
+ self.__source_with_updating_toggled)
self._setup_browse_button(self.source_path_browse, self.source_path,
_('Audit Log File'),
gtk.FILE_CHOOSER_ACTION_OPEN)
@@ -102,13 +105,20 @@
self.source_with_rotated.set_active(False)
self.source_type_file.set_active(True)
self.source_path.set_text(source.path)
+ elif isinstance(source, event_source.FileWithRotatedEventSource):
+ self.source_with_rotated.set_active(True)
+ self.source_type_file.set_active(True)
+ self.source_path.set_text(source.base)
else:
assert isinstance(source,
- event_source.FileWithRotatedEventSource), \
+ event_source.UpdatableEventSource), \
'Unexpected event source'
self.source_with_rotated.set_active(True)
+ self.source_with_rotated.set_sensitive(False)
+ self.source_type_log.set_sensitive(False)
self.source_type_file.set_active(True)
self.source_path.set_text(source.base)
+ self.source_with_updating.set_active(True)
def save(self, main_window):
'''Modify main_window to reflect dialog state.'''
@@ -124,7 +134,10 @@
source = event_source.ClientEventSource(self.client, name)
else:
path = self.source_path.get_text()
- if self.source_with_rotated.get_active():
+ if self.source_with_updating.get_active():
+ source = event_source.UpdatableEventSource(path)
+ main_window.updater = gobject.idle_add(main_window.update_tabs)
+ elif self.source_with_rotated.get_active():
source = event_source.FileWithRotatedEventSource(path)
else:
source = event_source.FileEventSource(path)
@@ -175,11 +188,21 @@
if it is not None:
self.source_log.set_active_iter(it)
+ def __source_with_updating_toggled(self, *_):
+ is_set = self.source_with_updating.get_active()
+ self.source_with_rotated.set_sensitive(not is_set)
+ self.source_type_log.set_sensitive(not is_set)
+ if is_set:
+ self.source_type_file.set_active(True)
+ self.source_type_log.set_active(False)
+ self.source_with_rotated.set_active(True)
+
def __source_type_file_toggled(self, *_):
util.set_sensitive_all(self.source_type_file.get_active(),
self.source_path_label, self.source_path,
self.source_path_browse)
+
def __window_destroy(self, *_):
self.emit('destroy')
return False
diff -u -X ex or_src/event_source.py oav/src/event_source.py
--- or_src/event_source.py 2009-06-09 23:10:41.000000000 +0400
+++ oav/src/event_source.py 2015-03-30 12:41:11.028004422 +0300
@@ -19,7 +19,7 @@
import datetime
import os.path
import re
-
+import os
import auparse
__all__ = ('ClientEventSource', 'ClientWithRotatedEventSource',
@@ -95,7 +95,7 @@
'''A source of audit events, reading from an auparse
parser.'''
def read_events(self, filters, wanted_fields, want_other_fields,
- keep_raw_records):
+ keep_raw_records, direction = None, tab = None):
'''Return a sequence of audit events read from parser.
Use filters to select events. Store wanted_fields in event.fields, the
@@ -265,6 +265,107 @@
def _create_parser(self):
return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.str)
+class UpdatableEventSourceReader():
+
+ '''A separate reader of audit events, created for each tab of main
window.
+ To be used with UpdatableEventSource.
+
+ '''
+
+ def __init__(self, event_source, tab):
+ self.event_source = event_source
+ self.tab = tab
+ self.up_file = open(event_source.base)
+ self.bottom_file = open(event_source.base)
+ self.up_file.seek(0, 2)
+ self.up_pos = self.down_pos = self.up_file.tell()
+ self.bytes = self.event_source.avg_line_length * self.event_source.chunk_size
+
+ def read_down(self):
+ ''' read older lines from file starting from
self.down_pos'''
+ if os.path.exists(self.bottom_file.name):
+ if os.stat(self.bottom_file.name).st_ino !=
os.fstat(self.bottom_file.fileno()).st_ino:
+ self.bottom_file.close()
+ self.tab.want_read_down = False
+ return ''
+ if self.down_pos <= self.bytes:
+ self.bottom_file.seek(0)
+ lines = self.bottom_file.read(self.down_pos)
+ try:
+ files = os.listdir(os.path.dirname(self.event_source.base))
+ files = sorted(files, key = lambda x : os.stat(x).st_mtime)
+ filename = self.bottom_file.name
+ self.bottom_file.close()
+ self.bottom_file = open(files[files.index(filename) + 1])
+ self.bottom_file.seek(0, 2)
+ self.down_pos = self.bottom_file.tell()
+ except:
+ self.tab.want_read_down = False
+ else:
+ self.bottom_file.seek(self.down_pos - self.bytes)
+ lines = self.bottom_file.read(self.bytes)
+ lines = lines[lines.find('\n') + 1:]
+ self.down_pos -= len(lines)
+ return lines
+
+ def read_up(self):
+ '''try to read new lines if there are any after the previous
reads'''
+ if os.path.exists(self.up_file.name):
+ if os.stat(self.up_file.name).st_ino !=
os.fstat(self.up_file.fileno()).st_ino:
+ self.up_file.close()
+ self.up_file = open(self.event_source.base)
+ self.up_pos = 0
+ else:
+ self.up_file.close()
+ self.tab.want_read_up = False
+ return ''
+ self.up_file.seek(0, 2)
+ file_end_pos = self.up_file.tell()
+ if self.up_pos != file_end_pos:
+ self.up_file.seek(self.up_pos)
+ if file_end_pos - self.up_pos > self.bytes*5:
+ lines = self.up_file.read(self.bytes)
+ file_end_pos = self.up_file.tell()
+ else:
+ lines = self.up_file.read()
+ if lines.endswith('\n'):
+ self.up_pos = file_end_pos
+ else:
+ lines = lines[:lines.rfind('\n') + 1]
+ self.up_pos += len(lines)
+ return lines
+ return ''
+
+
+class UpdatableEventSource(_ParserEventSource):
+
+ def __init__(self, base_file, chunk_size = 50, avg_line_length = 74):
+ self.chunk_size = chunk_size
+ self.avg_line_length = avg_line_length
+ self.readers = {}
+ self.base = base_file
+ self.current_lines = ''
+
+ def add_tab(self, tab):
+ self.readers[tab] = UpdatableEventSourceReader(self, tab)
+
+ def remove_tab(self, tab):
+ del self.readers[tab]
+
+ def read_events(self, filters, wanted_fields, want_other_fields,
+ keep_raw_records, direction, tab):
+ if direction == 'up':
+ self.current_lines = self.readers[tab].read_up()
+ else:
+ self.current_lines = self.readers[tab].read_down()
+ return _ParserEventSource.read_events(self, filters, wanted_fields,
want_other_fields,
+ keep_raw_records)
+
+
+ def _create_parser(self):
+ return auparse.AuParser(auparse.AUSOURCE_BUFFER, self.current_lines)
+
+
def check_expression(expr):
'''Check expr.
diff -u -X ex or_src/list_tab.py oav/src/list_tab.py
--- or_src/list_tab.py 2009-12-19 10:00:00.000000000 +0300
+++ oav/src/list_tab.py 2015-03-30 13:26:44.579005427 +0300
@@ -28,6 +28,7 @@
from search_entry import SearchEntry
from tab import Tab
import util
+import event_source
__all__ = ('ListTab')
@@ -130,7 +131,7 @@
date_column_label = '__audit_viewer_date'
__list_number = 1
- def __init__(self, filters, main_window, will_refresh = False):
+ def __init__(self, filters, main_window, will_refresh = True):
Tab.__init__(self, filters, main_window, 'list_vbox')
# date_column_label == event date, None == all other columns
@@ -157,6 +158,13 @@
util.connect_and_run(self.selection, 'changed',
self.__selection_changed)
+ if isinstance(self.main_window.event_source, event_source.UpdatableEventSource):
+ self.main_window.event_source.add_tab(self)
+ self.want_read_up = True
+ self.want_read_down = True
+ self.__refresh_dont_read_events = False
+ self.refresh(False, 'up')
+ return
self.__refresh_dont_read_events = will_refresh
self.refresh()
self.__refresh_dont_read_events = False
@@ -191,20 +199,25 @@
% (util.filename_to_utf8(filename),
e.strerror))
- def refresh(self):
- event_sequence = self.__refresh_get_event_sequence()
+ def refresh(self, updating = False, direction = None):
+ if isinstance(self.main_window.event_source, event_source.UpdatableEventSource)
and not updating:
+ self.want_read_down = True
+ self.want_read_up = True
+ self.main_window.event_source.readers[self] =
event_source.UpdatableEventSourceReader(self.main_window.event_source, self)
+ event_sequence = self.__refresh_get_event_sequence(direction, self)
if event_sequence is None:
return
-
- if self.filters:
- t = _(', ').join(f.ui_text() for f in self.filters)
- else:
- t = _('None')
- self.list_filter_label.set_text(t)
- self.__refresh_update_tree_view()
+ if not updating:
+ if self.filters:
+ t = _(', ').join(f.ui_text() for f in self.filters)
+ else:
+ t = _('None')
+ self.list_filter_label.set_text(t)
+ self.__refresh_update_tree_view()
events = self.__refresh_collect_events(event_sequence)
- self.__refresh_update_store(events)
+ self.__refresh_update_store(events, updating)
+
def report_on_view(self):
self.main_window.new_report_tab(self.filters)
@@ -462,7 +475,7 @@
for record in event.records
for (key, value) in record.fields]))
- def __refresh_get_event_sequence(self):
+ def __refresh_get_event_sequence(self, direction = None, tab = None):
'''Return an event sequence (as if from
self.main_window.read_events()).
Return None on error.
@@ -480,7 +493,7 @@
elif title is not self.date_column_label:
wanted_fields.add(title)
return self.main_window.read_events(self.filters, wanted_fields,
- want_other_fields, True)
+ want_other_fields, True, direction, tab)
def __refresh_update_tree_view(self):
'''Update self.list_tree_view for current configuration.
@@ -560,7 +573,32 @@
events.sort(key = lambda event: event[0], reverse = self.sort_reverse)
return events
- def __refresh_update_store(self, events):
+ def __insert_row(self, event):
+ ''' insert new row with event into self.store preserving sort
order'''
+ it = None
+ if not isinstance(self.main_window.event_source,
event_source.UpdatableEventSource):
+ it = self.store.append(event[1])
+ return it
+ if self.sort_by and self.sort_by not in self.columns:
+ self.sort_by = None
+ if self.sort_by:
+ sort_field = self.__field_columns.index(self.sort_by) + 1
+ for i in range(len(self.store)):
+ if event[0] > self.store[i][sort_field] and self.sort_reverse or \
+ event[0] <= self.store[i][sort_field] and not self.sort_reverse:
+ it = self.store.insert(i, event[1])
+ break
+ else:
+ for i in range(len(self.store)):
+ if event[1][0].id.sec > self.store[i][0].id.sec and self.sort_reverse
or \
+ event[1][0].id.sec <= self.store[i][0].id.sec and not
self.sort_reverse:
+ it = self.store.insert(i, event[1])
+ break
+ if not it:
+ it = self.store.append(event[1])
+ return it
+
+ def __refresh_update_store(self, events, updating = False):
'''Update self.store and related data.
events is the result of self.__refresh_collect_events().
@@ -571,11 +609,12 @@
key = pos.event_key
l = positions_for_event_key.setdefault(key, [])
l.append(pos)
- self.store.clear()
+ if not updating:
+ self.store.clear()
if (self.text_filter is None and
len(positions_for_event_key) == 0): # Fast path
for event in events:
- self.store.append(event[1])
+ self.__insert_row(event)
else:
event_to_it = {}
text_filter = self.text_filter
@@ -604,7 +643,8 @@
or (self.__other_column_event_text(event_tuple[0]).
find(self.text_filter) == -1))):
continue
- it = self.store.append(event_tuple)
+
+ it = self.__insert_row(event)
event_id = event_tuple[0].id
key = (event_id.serial, event_id.sec, event_id.milli)
if key in positions_for_event_key:
diff -u -X ex or_src/main.py oav/src/main.py
--- or_src/main.py 2008-06-26 00:17:59.000000000 +0400
+++ oav/src/main.py 2015-03-25 15:31:29.663932132 +0300
@@ -29,6 +29,7 @@
from main_window import MainWindow
import settings
import util
+import event_source
_ = gettext.gettext
@@ -48,12 +49,20 @@
help = _('do not attempt to start the privileged backend '
'for reading system audit logs'))
parser.set_defaults(unprivileged = False)
+ parser.add_option('-p', '--updatable', action =
'store_true',
+ dest = 'updatable',
+ help = _('read new lines from log '))
+ parser.set_defaults(updatable = False)
+ parser.add_option('-s', '--source', type = 'string',
+ dest = 'source',
+ help = _('path to log file '))
(options, args) = parser.parse_args()
gnome.init(settings.gettext_domain, settings.version)
gtk.glade.bindtextdomain(settings.gettext_domain, settings.localedir)
gtk.glade.textdomain(settings.gettext_domain)
+ ev_source = None
if options.unprivileged:
cl = None
else:
@@ -66,7 +75,9 @@
sys.exit(1)
except client.ClientNotAvailableError:
cl = None
+ if options.updatable:
+ ev_source = event_source.UpdatableEventSource(options.source)
- w = MainWindow(cl)
+ w = MainWindow(cl, ev_source)
if w.setup_initial_window(args):
gtk.main()
diff -u -X ex or_src/main_window.py oav/src/main_window.py
--- or_src/main_window.py 2008-08-19 14:38:16.000000000 +0400
+++ oav/src/main_window.py 2015-03-30 13:21:25.607005136 +0300
@@ -135,6 +135,8 @@
'''
try:
+ if isinstance(self.event_source, event_source.UpdatableEventSource):
+ self.updater = gobject.idle_add(self.update_tabs)
if isinstance(self.event_source, event_source.EmptyEventSource):
self.__event_error_report_only_one_push()
if self.client is not None:
@@ -246,7 +248,7 @@
return (filename, extension)
def read_events(self, filters, wanted_fields, want_other_fields,
- keep_raw_records):
+ keep_raw_records, direction = None, tab = None):
'''Read audit events.
Return a sequence of events, or None on error (without throwing
@@ -262,7 +264,7 @@
try:
return self.event_source.read_events(filters, wanted_fields,
want_other_fields,
- keep_raw_records)
+ keep_raw_records, direction, tab)
except IOError, e:
if (self.__event_error_report_only_one_depth == 0 or
not self.__event_error_reported):
@@ -381,16 +383,30 @@
'''End a region in which only one error message should be
reported.'''
self.__event_error_report_only_one_depth -= 1
- def __refresh_all_tabs(self):
+ def update_tabs(self):
+ self.__refresh_all_tabs(True)
+ return True
+
+ def __refresh_all_tabs(self, updating = False):
'''Refresh all tabs, taking care to report errors only
once.'''
self.__event_error_report_only_one_push()
try:
- for page_num in xrange(self.main_notebook.get_n_pages()):
- tab = self.__tab_objects[self.main_notebook
- .get_nth_page(page_num)]
- tab.refresh()
+ if not updating:
+ for page_num in xrange(self.main_notebook.get_n_pages()):
+ tab = self.__tab_objects[self.main_notebook
+ .get_nth_page(page_num)]
+ tab.refresh()
+ else:
+ for page_num in xrange(self.main_notebook.get_n_pages()):
+ tab = self.__tab_objects[self.main_notebook
+ .get_nth_page(page_num)]
+ if tab.want_read_up:
+ tab.refresh(True, 'up')
+ if tab.want_read_down:
+ tab.refresh(True, 'down')
finally:
self.__event_error_report_only_one_pop()
+ return True
def __menu_new_list_activate(self, *_):
self.new_list_tab([])
----- Исходное сообщение -----
От: "mitr" <mitr(a)redhat.com>
Кому: "Xeniya Muratova" <muratova(a)itsirius.su>
Копия: "linux-audit" <linux-audit(a)redhat.com>
Отправленные: Среда, 4 Март 2015 г 20:50:53
Тема: Re: log rendering in real time in audit-viewer
Hello,
Hello Miloslav, and all the guys!
We use audit-viewer for events monitoring.
Unfortunately, if some log is rather big it takes to much time for
audit-viewer to parse and render it.
Besides, we need to render log updates in real time, i.e. when a new line
appears in a log, it should appear in a viewer too.
Can you suggest the better way to extend audit-viewer to meet these
requirements?
Well, write the code? Something like inotify could be useful. There isn’t any hidden
switch to enable these features, if that is what you are asking.
As for performance, I may have missed something but I think I have squeezed as much as can
be done with Python; improving performance further would very likely require a C
extension.
(audit-viewer is a PyGtk2 application, and at I’m afraid I don’t currently have plans to
port it to GTK+3/gobject-introspection or do any other non-trivial work on the project, at
least in the near term.)
Mirek