a ruby script for scanning the system befindlichkeit of a linux system
a small ruby script to scans logfiles, /proc/stat and /proc/net/dev for data and spits out osc messages for these:
# scans /proc/stat and /proc/net/dev
# spits aout osc messages if there are RELATIVE CHNAGES ONLY
# since most/all of the data in these files is counters
# we keep a back reference to the data scanned in the loop before,
# calculate differences and if these are > 0 the fire the messages
# (c) 2003 andi@mego.at
# part of the gull
# code covered by the GPL
require 'oscsend'
# this is the sleep value between consecutive loops
SCANDELAY = 0.05
# the tail command used to tail
# this reqires special privileges on proper systems.
TAILCOMMAND = 'sudo tail -f -s 0.01 /var/log/syslog /var/log/mail.log'
if ARGV.length != 2
print "usage: stat.rb send-to-host portno\n"
exit
end
sendhost = ARGV[0].to_s
sendport = ARGV[1].to_i
hostname = `hostname -f`
hostname.chop!
print "sending from #{hostname} to #{sendhost} #{sendport}\n"
# the actions hash holds actions for the different stat data types.
actions = Hash.new(proc {})
# keep a reference to the previous raw data
prevdata = Hash.new
# keep a reference to the previous sent data (difference values)
# is used to decide if we send data
prevsent = Hash.new
# fill the actions array. used only for stat data, net data is handled
# differently
actions["cpu"] = proc {
|data|
x = prevdata["cpu"]
send = Array.new
data.length.times {
|i|
send[i] = data[i] - x[i]
}
send
}
# only scan the first 16 interrupts
actions["intr"] = proc {
|data|
x = prevdata["intr"]
send = Array.new
16.times {
|i|
send[i] = data[i] - x[i]
}
send
}
actions["processes"] = proc {
|data|
x = prevdata["processes"]
send = Array.new
data.length.times {
|i|
send[i] = data[i] - x[i]
}
send
}
actions["page"] = proc {
|data|
x = prevdata["page"]
send = Array.new
data.length.times {
|i|
send[i] = data[i] - x[i]
}
send
}
actions["swap"] = proc {
|data|
x = prevdata["swap"]
send = Array.new
data.length.times {
|i|
send[i] = data[i] - x[i]
}
send
}
# for logfile-tailer
def process_data(data)
case data
when /-- MARK --/
return ["MARK"]
when /^last/
return ["repeat"]
when /kernel:/
info = /kernel: (.*)/.match(data)[1].to_s()
return ["kernel", /^kernel: (.*)/.match(data)[1].to_s()]
else
info = /^([^ [:]*)/.match(data)[1].to_s()
return [info]
end
end
###################
#### main loop
# create logfile tailer thread (lightweigth ruby thread..)
tthread = Thread.new(sendhost, sendport) {
| sendhost, sendport |
lprevtime = Hash.new
r = Regexp.new("^([^ ]* *[^ ]* *[^ ]*) *([^ ]*) *(.*)")
f = IO.popen(TAILCOMMAND).each() {
|l|
if l != "\n" && !(/^==/.match(l)) # wipe "==> bla <==" from tailing more logfiles
m = r.match(l)
time = m[1]
host = m[2]
data = process_data(m[3])
# $stdout << host << " " << time << "\n" << data[0] << "\n"
if lprevtime[data[0]] == nil
lprevtime[data[0]] = Time.new
end
timediff = Time.new.to_f - lprevtime[data[0]].to_f
Osc::OscMessage.new("/sysmon/#{hostname}/log", data + [timediff,time]).send_to("192.168.1.52", 5151)
end
}
f.close()
}
while 1
# check /proc/stat
lines = IO.readlines("/proc/stat")
lines.each {
| line |
items = line.split
what = items[0]
data = items[1..items.length]
# convert data to integer types
data.collect! {|x| x.to_i}
# fill an initial prevdata
if prevdata[what] == nil
prevdata[what] = data.dup
end
# call the matching method in actions hash
s = actions[what].call(data)
# check if its time to send
if prevsent[what] != s
Osc::OscMessage.new("/sysmon/"+hostname+"/proc", [what] + s).send_to(sendhost, sendport)
prevsent[what] = s.dup
end
# store previous data for the next loop
prevdata[what] = data.dup
}
# check /proc/net/dev
lines = IO.readlines("/proc/net/dev")
# weed first two lines (headers)
lines = lines[2..lines.length]
lines.each {
| line |
# substitute the colon after the interface name
line.sub!(/:/, ' ')
items = line.split
what = items[0]
data = items[1..items.length]
data.collect! {|x| x.to_i}
if prevdata[what] == nil
prevdata[what] = data.dup
end
x = prevdata[what]
s = Array.new
data.length.times {
|i|
s[i] = data[i] - x[i]
}
# this does not happen usually, could be left out. theres always
# activity ion the net..
if prevsent[what] != s
Osc::OscMessage.new("/sysmon/"+hostname+"/net", [what]+s).send_to(sendhost, sendport)
prevsent[what] = s.dup
end
prevdata[what] = data.dup
}
# zzzzz for scandelay seconds
sleep(SCANDELAY)
end
-- Main.andi - 02 Jun 2003
to top