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