MHo: Program for
tailing ms windows event logs using twapi. Work in progress. use it in any way you want, but at your own risk. This is the main prog of a starpack which will later be made downloadable somewhere.
package provide app-wineltail 1.0
# wineltail
# 18.10.2013
# (c) Matthias Hoffmann
# Monitor Windows Event Logs
package require twapi_eventlog
package require twapi_console
::twapi::import_commands
proc trapClose {type} {
global hevl hmon hname signal
puts stderr $type
foreach h [array names hmon] {
puts stderr "Stoppe Monitor Instanz $hmon($h) für $hname($h)"
catch {eventlog_monitor_stop $hmon($h)}
}
foreach h [array names hevl] {
puts stderr "Schliesse Eventloginstanz $hevl($h) für $hname($h)"
catch {eventlog_close $hevl($h)}
}
set signal 1
}
proc formatSID {sid} {
if {[string length $sid]} {
if {![catch {lookup_account_sid $sid} rc]} {
return $rc
}
}
return [string range $sid end-6 end]
}
proc formatEventID {eid} {
# diese merkwürdige Logik muss auch noch gecheckt werden!
set e [expr {$eid-1073741824}]
if {$e < 0} {
set e 1
}
return $e
}
# Idlehandler
proc formatOutput {l lbuf} {
foreach eventrec $lbuf {
array set event $eventrec
# $event(-sid) später nur optional!! Bzw. Ausgabe gemäss variabler Schablone.
# Verwendung von lookup_account_sid direkt in eventSink führt zum Verschlucken von Events,
# daher hier asynchron
# [list [string range $event(-sid) end-6 end]]
puts "[list [clock format $event(-timegenerated) -format {%Y%m%d %T}]] \
[list $event(-system)] \
[list [formatSID $event(-sid)]] \
[list $l] \
[list $event(-recordnum)] \
[list [string range $event(-type) 0 6]] \
[list $event(-source)] \
[list [eventlog_format_category $eventrec -width -1]] \
[list [formatEventID $event(-eventid)]] \
[list [eventlog_format_message $eventrec -width -1]]"
array set event {}
}
}
# der Monitorhandle wird immer als letztes Übergeben (nicht benötigt)
# wird nur gefeuert, wenn tatsächlich ein Signal anliegt
proc eventSink {e l m} {
after idle [list formatOutput $l [eventlog_read $e]]; # asynchron weitermachen
}
# für eigenes Polling
proc eventPoll {e l} {
after idle [list formatOutput $l [eventlog_read $e]]; # asynchron weitermachen
after 5000 [info level 1]; # später Intervall bestimmbar machen
}
if {$argc == 0} {
puts stderr {Parameter: (engl.) Name(n) der zu überwachenden Eventquelle(n), Bsp.: System
Es können mehrere Quellen angegeben werden, Bsp.: System Application
Es kann je Quelle ein Systemname angegeben werden, Bsp.: "System Matthias" Application
(in diesem Fall wird versucht, die System-Events vom remote Rechner Matthias zu lesen,
und Application-Events vom lokalen Rechner).
Das Auflösen von Benutzernamen funktioniert ggF. nicht. Es werden dann die letzten Stellen
der SID zurückgegeben.
Das Programm kann mit Strg+C beendet werden.
}
exit 255
}
set_console_control_handler [list trapClose]
set ix 0
array set hevl {}
array set hmon {}
array set hname {}
foreach log $argv {
if {[llength $log] == 2} {
set logname [lindex $log 0]
set logsys [lindex $log 1]
} else {
set logname $log
set logsys $::env(computername)
}
if {![catch {set hevl($ix) [eventlog_open -source $logname -system $logsys]} rc]} {
if {[catch {
# zu Beginn die jüngsten 10 Sätze je Log anzeigen
formatOutput $logname [twapi::eventlog_read $hevl($ix) -seek [expr {[twapi::eventlog_oldest $hevl($ix)]+[twapi::eventlog_count $hevl($ix)]-10}]]
if {[catch {set hmon($ix) [eventlog_monitor_start $hevl($ix) [list eventSink $hevl($ix) $logname]]}]} {
# Monitoring auf remote_logs funktioniert nicht, also in diesem Falle eigenes Polling
eventPoll $hevl($ix) $logname
}
set hname($ix) "$logsys $logname"; # nur für Anzeige beim späteren Schließen
incr ix
} rc]} {
puts stderr "(2) $rc"
}
} else {
puts stderr "(1) $rc"; # im Heimnetz nicht testbar!? ("der RPC-Server ist nicht verfpgbar")
}
}
if {$ix == 0} {
puts stderr "Es konnten keine Logs geöffnet werden!"
exit 1
}
vwait signal
exit 0