]> git.immae.eu Git - perso/Immae/Projets/Ruby/Monitor.git/blame - monitor.rb
Different initialization file location
[perso/Immae/Projets/Ruby/Monitor.git] / monitor.rb
CommitLineData
8b6154e9
I
1#!/usr/bin/env ruby
2
3require "time"
4require "ncurses"
5require "inifile"
6require "open3"
cfddfd90
I
7require 'optparse'
8require 'ostruct'
8b6154e9
I
9
10class IO
11 def readline_nonblock
12 buffer = ""
13 buffer << read_nonblock(1) while buffer[-1] != "\n"
14 buffer
15 rescue IO::WaitReadable => blocking
16 if (not buffer.empty?)
17 ungetc(buffer)
18 end
19 raise blocking
20 end
21end
22
cfddfd90
I
23class OptParse
24 def parse(args)
25
26 options = OpenStruct.new()
27 options.inifile = nil
28
29 opt_parser = OptionParser.new() do |opts|
30
31 opts.banner = "Usage: monitor [options]"
32 opts.separator ""
33
34 opts.on( '-f', "-f ini_file", "chose a different initialization file") do |ini|
35 options.inifile = ini
36 end
37 end
38 opt_parser.parse!(args)
39 return options
40 end
41end
42
8b6154e9
I
43class List_Win
44 def initialize(inifile)
45 @params = inifile
46 @win = Ncurses::WINDOW.new(0, Ncurses.COLS()/4, 0, 0)
47 @win.border(*([0]*8))
48 @win.keypad(true)
49 @win.timeout(1000)
50 @win.refresh()
51 @entry = 0
52 end
53
54 def getch()
55 @win.getch()
56 end
57 def clear()
58 @win.clear()
59 end
60 def resize(x,y)
61 @win.resize(x,y)
62 end
63 def move(x,y)
64 @win.move(x,y)
65 end
66 def refresh()
67 @win.refresh()
68 end
69
70 def print_list(entry=nil)
71 if(not entry.nil?)
72 @entry = entry
73 end
74 i = 0
75 @params.each_section do |section|
76 if(@entry == i)
77 @win.attron(Ncurses::A_REVERSE)
78 end
79 @win.move(i+1,1)
80 print_line(@win,@params[section]['Name'])
81 if(@entry == i)
82 @win.attroff(Ncurses::A_REVERSE)
83 end
84 i = i+1
85 end
86 @win.border(*([0]*8))
87 @win.move(0,3)
88 @win.addstr("Menu")
89 @win.refresh()
90 end
91end
92
93class Buff_Win
94 def initialize(winsize,winpos,params)
95 @params = params
96 @win = Ncurses::WINDOW.new(0, winsize, 0, winpos)
97 @panel = Ncurses::Panel::PANEL.new(@win)
98 if(@params['Type'] == 'oneshot')
99 @buffer = Buffer.new(0)
100 else
101 @buffer = Buffer.new(@params['Buffer'].to_i)
102 end
103 @proc = nil
104 @curr_offset = 0
105 @hscroll = 0
106 update_date()
107
108 spawn_proc()
109 print_buffer()
110 end
111
112 def move_resize(winsize,winpos)
113 newwin = Ncurses::WINDOW.new(0, winsize, 0, winpos)
114 Ncurses::Panel.replace_panel(@panel, newwin)
115 Ncurses.delwin(@win)
116 @win = newwin
117 print_buffer()
118 end
119 def update_date()
120 @last_update = Time.now
121 @date = @last_update.strftime("%F %R:%S")
122 end
123
124 def win_border()
125 @win.border(*([0]*8))
126 win_header()
127 end
128 def win_header()
129 @win.move(0,3)
130 @win.addnstr(@params['Name'],@win.getmaxx-@date.length-10)
131 @win.move(0,@win.getmaxx-@date.length-2)
132 @win.addstr(@date)
133 end
134 def refresh()
135 @win.refresh()
136 end
137 def clear()
138 @win.clear()
139 end
140
141 def show_win()
142 Ncurses::Panel.top_panel(@panel)
143 Ncurses::Panel.update_panels
144 end
145
146 def hscroll(scroll=0)
147 @hscroll += scroll
148 if(@hscroll < 0)
149 @hscroll = 0
150 end
151 if(@hscroll > @buffer.maxlen()-@win.getmaxx+3)
152 @hscroll = @buffer.maxlen()-@win.getmaxx+3
153 end
154 print_buffer()
155 refresh()
156 end
157 def scroll(scroll=0,goto=nil,fact=nil)
158 if (not fact.nil?)
159 scroll = (fact * @win.getmaxy.to_f).to_i
160 elsif (not goto.nil?)
161 @curr_offset = (goto * @buffer.size()).to_i
162 scroll = 0
163 end
164 #@curr_offset -= @win.getmaxy/2
165 @curr_offset += scroll
166 if(@curr_offset < 0)
167 @curr_offset = 0
168 end
169 if(@curr_offset > @buffer.size()-@win.getmaxy+2)
170 @curr_offset = @buffer.size()-@win.getmaxy+2
171 end
172 print_buffer()
173 refresh()
174 end
175
176 def print_buffer()
9b96f7cb 177 #clear()
8b6154e9
I
178 win_border()
179 j = 1
180 @buffer.yield(@win.getmaxy-2,@curr_offset) { |l,type|
181 @win.move(j,1)
182 if(type == 1) then @win.attron(Ncurses.COLOR_PAIR(1)) end
183 print_line(@win,l,hscroll=@hscroll)
184 if(type == 1) then @win.attroff(Ncurses.COLOR_PAIR(1)) end
185 j = j+1
186 }
187 if(@buffer.has_before?)
188 @win.move(2,@win.getmaxx-1)
189 @win.attron(Ncurses::A_REVERSE)
190 @win.addstr("↑")
191 @win.attroff(Ncurses::A_REVERSE)
192 end
193 if(@buffer.has_after?)
194 @win.move(@win.getmaxy-2,@win.getmaxx-1)
195 @win.attron(Ncurses::A_REVERSE)
196 @win.addstr("↓")
197 @win.attroff(Ncurses::A_REVERSE)
198 end
199 end
200
201 def proc_readlines()
202 begin
203 while true
204 @buffer.push(@proc.readline_nonblock)
205 update_date()
206 end
207 rescue IO::WaitReadable
208 end
209 end
210
9b96f7cb 211 def update(force=false)
8b6154e9
I
212 if(@params['Type'] == 'continuous')
213 proc_readlines()
214 end
215 if(@params['Type'] == 'oneshot')
9b96f7cb 216 if(force or (Time.now - @last_update > @params['Periodic'].to_i))
8b6154e9
I
217 @buffer.clear()
218 spawn_proc()
cfddfd90 219 clear()
8b6154e9
I
220 end
221 end
222 print_buffer()
223 end
224
225 def spawn_proc()
226 if(@params['Type'] == 'oneshot')
227 update_date()
228 Open3.popen3(@params["Command"]) { |i,o,e,t|
229 while ((not o.eof?) or (not e.eof?))
230 rs = IO.select([o,e],nil)[0]
231 r = (rs[0].eof?)? rs[1] : rs[0]
232
233 if r.fileno == o.fileno
234 @buffer.push(r.readline)
235 elsif r.fileno == e.fileno
236 @buffer.push(r.readline,type=1)
237 end
238 end
239 }
240 elsif(@params['Type'] == 'continuous')
241 @proc = IO.popen(@params["Command"])
242 proc_readlines()
243 end
244 end
245end
246
247class Buffer
248 def initialize(size)
249 @size = size
250 @buff = []
251 @buff_type = []
252 @current = 0
253 @wrap = false
254 @before = false
255 @after = false
256 end
257 def size()
258 return @buff.length
259 end
260 def maxlen()
261 maxlen = 0
262 @buff.each do |string|
263 if string.length > maxlen
264 maxlen = string.length
265 end
266 end
267 return maxlen
268 end
269
270 def push(string,type=0)
271 if(string.chomp.empty?) then string = " " end
272 string.split( /\r?\n/ ).each do |line|
273 @buff[@current] = line
274 @buff_type[@current] = type
275 if(@size > 0)
276 @current = (1+@current) % @size
277 else
278 @current = 1+@current
279 end
280 if(@current == 0) then @wrap = true end
281 end
282 end
283 def yield(size,offset=0,&block)
9b96f7cb 284 if(size < 0) then size = 0 end
8b6154e9
I
285 range = Range.new(0,@current-1).to_a
286 if(@wrap)
287 range = Range.new(@current,@size-1).to_a + range
288 end
289 range = range.last(size+offset)[0,size]
290 @before = (size+offset < @buff.length)
291 @after = (offset != 0 and size < @buff.length)
292 if(block)
293 range.each do |i|
294 yield [@buff[i],@buff_type[i]]
295 end
296 else
297 return range.collect{|r| [@buff[r],@buff_type[r]]}
298 end
299 end
300 def has_after?()
301 return @after
302 end
303 def has_before?()
304 return @before
305 end
306 def clear()
307 @current = 0
308 @buff = []
309 @buff_type = []
310 end
311end
312
cfddfd90
I
313def find_ini(option_inifile)
314 if(not option_inifile.nil?)
315 inifile = option_inifile
316 elsif(ENV.has_key?('MONITOR_RC'))
317 inifile = ENV['MONITOR_RC']
318 elsif(Process.uid == 0)
319 inifile = "/etc/monitor.rc"
320 else
321 inifile = ENV['HOME']+"/.monitorrc"
322 end
323 return inifile
324end
325
326def read_ini(ini)
327 inifile = IniFile.load(ini)
328 if(inifile.nil?)
329 puts "Initialization file not found or not readable"
330 exit
331 end
8b6154e9
I
332 return inifile
333end
334
335def print_line(win, str, hscroll=0)
9b96f7cb
I
336 revert_color = false
337 str[0,5].match(/\033\[3(.)m/) { |c| #Line starts with an escape sequence. We handle that `a la xterm`
338 Ncurses.init_pair(10, c[1].to_i, Ncurses::COLOR_BLACK)
339 win.attron(Ncurses.COLOR_PAIR(10))
340 revert_color = true
341 str = str[5,str.length]
342 }
343 str = str.gsub("\011"," ")
344 #Any other control char is ignored and escaped
345 str = str.gsub(/[[:cntrl:]]/) { |m|
346 "^"+(m.ord + 64).chr
347 }
8b6154e9
I
348 if(hscroll > 0)
349 strcut = str[hscroll,str.length]
350 if(strcut.nil? or strcut.empty?)
351 str = ""
352 else
353 str = "…"+strcut
354 end
355 end
356 strlen = str.length
357 winlen = win.getmaxx-win.getcurx-1
358 if(strlen <= winlen)
359 win.addstr(str + " "*(winlen-strlen))
360 else
361 win.addstr(str[0,winlen-1]+"…")
362 end
9b96f7cb
I
363 if(revert_color)
364 win.attroff(Ncurses.COLOR_PAIR(10))
365 end
8b6154e9
I
366end
367
368def make_bufwins(inifile)
369 bufwins = []
370 inifile.each_section do |section|
371 bufwin = Buff_Win.new(Ncurses.COLS()-Ncurses.COLS()/4,
372 Ncurses.COLS()/4,
373 inifile[section])
374 bufwins.push(bufwin)
375 end
376 return bufwins
377end
378
379def update_buffers(bufwins)
380 bufwins.each do |bufwin|
381 bufwin.update()
382 end
383end
384
385def redraw_all(list,bufwins,curr_bufwin)
386 bufwins.each do |bufwin|
387 bufwin.move_resize(Ncurses.COLS()-Ncurses.COLS()/4,Ncurses.COLS()/4)
388 end
389 list.resize(Ncurses.LINES(), Ncurses.COLS()/4)
390 list.clear()
391 list.print_list()
392 list.refresh()
393 curr_bufwin.refresh()
394end
cfddfd90
I
395
396
397options = OptParse.new().parse(ARGV)
398inifile = read_ini(find_ini(options.inifile))
8b6154e9
I
399begin
400 # initialize ncurses
401 Ncurses.initscr
402 Ncurses.start_color
403 Ncurses.cbreak # provide unbuffered input
404 Ncurses.noecho # turn off input echoing
405 #Ncurses.nonl # turn off newline translation
406 #Ncurses.stdscr.intrflush(false) # turn off flush-on-interrupt
407 Ncurses.stdscr.keypad(true) # turn on keypad mode
408 Ncurses.init_pair(1, Ncurses::COLOR_RED, Ncurses::COLOR_BLACK)
9b96f7cb 409 Ncurses.init_pair(10, Ncurses::COLOR_WHITE, Ncurses::COLOR_BLACK)
8b6154e9
I
410
411
8b6154e9
I
412 list = List_Win.new(inifile)
413 bufwins = make_bufwins(inifile)
414 entry = 0
415 cur_bufwin = bufwins[entry]
416 cur_bufwin.show_win()
417 list.print_list()
418 while(ch = list.getch()) do
419 case(ch)
420 when "n".ord
421 entry = (entry +1) % bufwins.length
422 cur_bufwin = bufwins[entry]
423 cur_bufwin.show_win()
424 list.print_list(entry=entry)
425 when "p".ord
426 entry = (entry -1) % bufwins.length
427 cur_bufwin = bufwins[entry]
428 cur_bufwin.show_win()
429 list.print_list(entry=entry)
430 when 12 #ctrl+L
431 redraw_all(list,bufwins,cur_bufwin)
9b96f7cb
I
432 when 18 #ctrl+R
433 cur_bufwin.update(force=true)
8b6154e9
I
434 when Ncurses::KEY_RESIZE
435 redraw_all(list,bufwins,cur_bufwin)
436 when Ncurses::KEY_LEFT
437 cur_bufwin.hscroll(scroll=-1)
438 when Ncurses::KEY_RIGHT
439 cur_bufwin.hscroll(scroll=1)
440 when Ncurses::KEY_DOWN
441 cur_bufwin.scroll(scroll=-1)
442 when Ncurses::KEY_UP
443 cur_bufwin.scroll(scroll=1)
444 when Ncurses::KEY_NPAGE
445 cur_bufwin.scroll(scroll=0,goto=nil,fact=-0.75)
446 when Ncurses::KEY_PPAGE
447 cur_bufwin.scroll(scroll=0,goto=nil,fact=0.75)
448 when Ncurses::KEY_HOME
449 cur_bufwin.scroll(scroll=0,goto=1.0)
450 when Ncurses::KEY_END
451 cur_bufwin.scroll(scroll=0,goto=0.0)
452 when Ncurses::ERR
453 update_buffers(bufwins)
454 cur_bufwin.show_win()
455 when "q".ord
456 break
457 else
458 next
459 end
460 end
461
462ensure
463 Ncurses.echo
464 Ncurses.nocbreak
465 Ncurses.nl
466 Ncurses.endwin
467end