/[rrd-simple-monitoring]/bin/rrd-client.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /bin/rrd-client.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Thu Jul 16 18:48:19 2009 UTC (14 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 39259 byte(s)
import upstream http://rrd.me.uk/rrd-simple-monitoring.tar.gz

without prerequisities

1 #!/usr/bin/perl -w
2 ############################################################
3 #
4 # $Id: rrd-client.pl 775 2006-10-08 18:47:33Z nicolaw $
5 # rrd-client.pl - Data gathering script for RRD::Simple
6 #
7 # Copyright 2006,2007 Nicola Worthington
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
12 #
13 # http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20 #
21 ############################################################
22 # vim:ts=4:sw=4:tw=78
23
24 ############################################################
25 # User defined constants
26 use constant DB_MYSQL_DSN => $ENV{DB_MYSQL_DSN} || 'DBI:mysql:mysql:localhost';
27 use constant DB_MYSQL_USER => $ENV{DB_MYSQL_USER} || undef;
28 use constant DB_MYSQL_PASS => $ENV{DB_MYSQL_PASS} || undef;
29
30 use constant NET_PING_HOSTS => $ENV{NET_PING_HOSTS} ?
31 (split(/[\s,:]+/,$ENV{NET_PING_HOSTS})) : qw();
32
33 #
34 # YOU SHOULD NOT NEED TO EDIT ANYTHING BEYOND THIS POINT
35 #
36 ############################################################
37
38
39
40
41
42 use 5.004;
43 use strict;
44 #use warnings; # comment out for release
45 use vars qw($VERSION);
46
47 $VERSION = '1.42' || sprintf('%d', q$Revision: 775 $ =~ /(\d+)/g);
48 $ENV{PATH} = '/bin:/usr/bin';
49 delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
50
51
52 # Default list of probes
53 my %probes = (
54 hw_irq_interrupts => 'Hardward IRQ Interrupts',
55
56 cpu_utilisation => 'CPU Utilisation',
57 cpu_loadavg => 'Load Average',
58 cpu_temp => 'CPU Temperature',
59 cpu_interrupts => 'CPU Interrupts',
60
61 hdd_io => 'Hard Disk I/O',
62 hdd_temp => 'Hard Disk Temperature',
63 hdd_capacity => 'Disk Capacity',
64
65 mem_usage => 'Memory Usage & Swap Usage',
66 mem_swap_activity => 'Swap Activity',
67 mem_proc_largest => 'Largest Process',
68
69 proc_threads => 'Threads',
70 proc_state => 'Processes',
71 proc_filehandles => 'File Handles',
72
73 apache_status => 'Apache Scoreboard & Apache Activity',
74 apache_logs => 'Apache Log Activity',
75
76 misc_uptime => 'Server Uptime',
77 misc_users => 'Users Logged In',
78 misc_ipmi_temp => 'IPMI Temperature Probes',
79 misc_entropy => 'Available Entropy',
80
81 db_mysql => 'MySQL Database Activity',
82 db_mysql_replication => 'MySQL Database Replication',
83
84 mail_exim_queue => 'Exim Mail Queue',
85 mail_postfix_queue => 'Postfix Mail Queue',
86 mail_sendmail_queue => 'Sendmail Mail Queue',
87
88 net_traffic => 'Network Traffic',
89 net_connections => 'Network Connections',
90 net_ping_host => 'Ping',
91 # net_connections_ports => 'Service Connections',
92 );
93
94
95 # Get command line options
96 my %opt = ();
97 eval "require Getopt::Std";
98 $Getopt::Std::STANDARD_HELP_VERSION = 1;
99 $Getopt::Std::STANDARD_HELP_VERSION = 1;
100 Getopt::Std::getopts('p:i:x:s:c:V:hvqlP:?', \%opt) unless $@;
101 (HELP_MESSAGE() && exit) if defined $opt{h} || defined $opt{'?'};
102 (VERSION_MESSAGE() && exit) if defined $opt{v};
103
104 # Display a list of available probe names
105 if ($opt{l}) {
106 print "Available probes:\n";
107 printf " %-24s %s\n",'PROBE','DESCRIPTION';
108 for (sort keys %probes) {
109 my $str = sprintf(" %-24s %s\n", $_, $probes{$_});
110 $str =~ s/(\S+) (\s+) /$_ = "$1 ". '.' x length($2) ." ";/e;
111 print "$str";
112 }
113 exit;
114 }
115
116 # Check to see if we are capable of SNMP queries
117 my $snmpClient;
118 if ($opt{s}) {
119 eval {
120 require Net::SNMP;
121 $snmpClient = 'Net::SNMP';
122 };
123 if ($@) {
124 my $c = 'snmpwalk'; # snmpget
125 my $cmd = select_cmd("/usr/bin/$c","/usr/local/bin/$c");
126 die "Error: unable to query via SNMP. Please install Net::SNMP or $c.\n" unless $cmd;
127 $snmpClient = $cmd;
128 }
129 $opt{c} = 'public' unless defined($opt{c}) && $opt{c} =~ /\S+/;
130 $opt{V} = '2c' unless defined($opt{V}) && $opt{V} =~ /^(1|2c)$/;
131 $opt{P} = 161 unless defined($opt{P}) && $opt{P} =~ /^[0-9]+$/;
132 }
133
134 # Filter on probe include list
135 my @probes = sort keys %probes;
136 if (defined $opt{i}) {
137 my $inc = join('|',split(/\s*,\s*/,$opt{i}));
138 @probes = grep(/(^|_)($inc)(_|$)/,@probes);
139 }
140
141 # Filter on probe exclude list
142 if (defined $opt{x}) {
143 my $exc = join('|',split(/\s*,\s*/,$opt{x}));
144 @probes = grep(!/(^|_)($exc)(_|$)/,@probes);
145 }
146
147
148 # Run the probes one by one
149 die "Error: nothing to probe!\n" unless @probes;
150 my $post = '';
151 my %update_cache;
152 for my $probe (@probes) {
153 eval {
154 local $SIG{ALRM} = sub { die "Timeout!\n"; };
155 alarm 15;
156 my $str = report($probe,eval "$probe();");
157 if (defined $opt{p}) {
158 $post .= $str;
159 } else {
160 print $str;
161 }
162 warn "Warning [$probe]: $@" if !$opt{q} && $@;
163 alarm 0;
164 };
165 warn "Warning [$probe]: $@" if !$opt{q} && $@;
166 }
167
168
169 # HTTP POST the data if asked to
170 print scalar(basic_http('POST',$opt{p},30,$post))."\n" if $opt{p};
171
172
173 exit;
174
175
176
177
178
179 # Report the data
180 sub report {
181 (my $probe = shift) =~ s/[_-]/\./g;
182 my %data = @_ % 2 ? (@_,undef) : @_;
183 my $str = '';
184 for my $k (sort keys %data) {
185 #$data{$k} = 0 unless defined($data{$k});
186 next unless defined($data{$k}) && $data{$k} =~ /^[0-9\.]*$/;
187 $str .= sprintf("%s.%s.%s %s\n", time(), $probe, $k, $data{$k});
188 }
189 return $str;
190 }
191
192
193 # Display help
194 sub HELP_MESSAGE {
195 print qq{Syntax: rrd-client.pl [-i probe1,probe2,..|-x probe1,probe2,..]
196 [-s host] [-c community] [-P port] [-V 1|2c] [-p URL] [-h|-v]
197 -i <probes> Include a list of comma seperated probes
198 -x <probes> Exclude a list of comma seperated probes
199 -s <host> Specify hostname to probe via SNMP
200 -c <community> Specify SNMP community name (defaults to public)
201 -V <version> Specify SNMP version to use (1 or 2c, defaults to 2c)
202 -P <port> Specify SNMP port to use
203 -p <URL> HTTP POST data to the specified URL
204 -q Suppress all warning messages
205 -l Display a list of available probe names
206 -v Display version information
207 -h Display this help
208
209 Examples:
210 rrd-client.pl -x apache_status -q -p http://rrd.me.uk/cgi-bin/rrd-server.cgi
211 rrd-client.pl -s localhost -p http://rrd.me.uk/cgi-bin/rrd-server.cgi
212 rrd-client.pl -s server1.company.com | rrd-server.pl -u server1.company.com
213 \n};
214 }
215
216
217 # Display version
218 sub VERSION { &VERSION_MESSAGE; }
219 sub VERSION_MESSAGE {
220 print "$0 version $VERSION ".'($Id: rrd-client.pl 775 2006-10-08 18:47:33Z nicolaw $)'."\n";
221 }
222
223
224 # Basic HTTP client if LWP is unavailable
225 sub basic_http {
226 my ($method,$url,$timeout,$data) = @_;
227 $method ||= 'GET';
228 $url ||= 'http://localhost/';
229 $timeout ||= 5;
230
231 my ($scheme,$host,$port,$path) = $url =~ m,^(https?://)([\w\d\.\-]+)(?::(\d+))?(.*),i;
232 $scheme ||= 'http://';
233 $host ||= 'localhost';
234 $path ||= '/';
235 $port ||= 80;
236
237 my $str = '';
238 eval "use Socket";
239 return $str if $@;
240
241 eval {
242 local $SIG{ALRM} = sub { die "TIMEOUT\n" };
243 alarm $timeout;
244
245 my $iaddr = inet_aton($host) || die;
246 my $paddr = sockaddr_in($port, $iaddr);
247 my $proto = getprotobyname('tcp');
248 socket(SOCK, AF_INET(), SOCK_STREAM(), $proto) || die "socket: $!";
249 connect(SOCK, $paddr) || die "connect: $!";
250
251 select(SOCK); $| = 1;
252 select(STDOUT);
253
254 # Send the HTTP request
255 print SOCK "$method $path HTTP/1.1\n";
256 print SOCK "Host: $host". ("$port" ne "80" ? ":$port" : '') ."\n";
257 print SOCK "User-Agent: $0 version $VERSION ".'($Id: rrd-client.pl 775 2006-10-08 18:47:33Z nicolaw $)'."\n";
258 if ($data && $method eq 'POST') {
259 print SOCK "Content-Length: ". length($data) ."\n";
260 print SOCK "Content-Type: application/x-www-form-urlencoded\n";
261 }
262 print SOCK "\n";
263 print SOCK $data if $data && $method eq 'POST';
264
265 my $body = 0;
266 while (local $_ = <SOCK>) {
267 s/[\n\n]+//g;
268 $str .= $_ if $_ && $body;
269 $body = 1 if /^\s*$/;
270 }
271 close(SOCK);
272 alarm 0;
273 };
274
275 warn "Warning [basic_http]: $@" if !$opt{q} && $@ && $data;
276 return wantarray ? split(/\n/,$str) : "$str";
277 }
278
279
280 # Return the most appropriate binary command
281 sub select_cmd {
282 foreach (@_) {
283 if (-f $_ && -x $_ && /(\S+)/) {
284 return $1;
285 }
286 }
287 return '';
288 }
289
290
291
292
293
294
295 #
296 # Probes
297 #
298
299
300 sub _snmp {
301 my $oid = [@_];
302 my $result = {};
303
304 # Net::SNMP
305 if ($snmpClient eq 'Net::SNMP') {
306 my ($session, $error) = Net::SNMP->session(
307 -hostname => $opt{s},
308 -community => $opt{c},
309 -version => $opt{V},
310 -port => $opt{P},
311 -translate => [ -timeticks => 0x0 ],
312 );
313 die $error if !defined($session);
314
315 $result = $session->get_request(-varbindlist => $oid);
316
317 $session->close;
318 die $session->error if !defined($result);
319
320 # snmpget / snmpwalk
321 } else {
322 my $oidStr = join(' ', @{$oid});
323 my $cmd = "$snmpClient -O n -O t -v $opt{V} -c $opt{c} $opt{s} $oidStr";
324 #my $cmd = "$snmpClient -O t -v $opt{V} -c $opt{c} $opt{s} $oidStr";
325
326 open(PH,'-|',"$cmd 2>&1") || die "Unable to open file handle PH for command '$cmd': $!\n";
327 while (local $_ = <PH>) {
328 s/[\r\n]+//g; s/^(?:\s+|\s+)$//g;
329 if (/^(\.[\.0-9]+|[A-Za-z:\-\.0-9]+)\s*=\s*(?:([A-Za-z0-9]+):\s*)?["']?(\S*)["']?/) {
330 my ($oid,$type,$value) = ($1,$2,$3);
331 $oid = ".$oid" if $oid =~ /^[0-9][0-9\.]+$/;
332 $result->{$oid} = $value;
333 } else {
334 warn "Warning [_snmp]: $_\n" unless $opt{q};
335 }
336 }
337 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
338 }
339
340 return $result->{(keys(%{$result}))[0]} if !wantarray && keys(%{$result}) == 1;
341 return $result;
342 }
343
344
345
346 sub net_ping_host {
347 return if $opt{s};
348 return unless defined NET_PING_HOSTS() && scalar NET_PING_HOSTS() > 0;
349 my $cmd = select_cmd(qw(/bin/ping /usr/bin/ping /sbin/ping /usr/sbin/ping));
350 return unless -f $cmd;
351 my %update = ();
352 my $count = 3;
353
354 for my $str (NET_PING_HOSTS()) {
355 my ($host) = $str =~ /^([\w\d_\-\.]+)$/i;
356 next unless $host;
357 my $cmd2 = "$cmd -c $count $host 2>&1";
358
359 open(PH,'-|',$cmd2) || die "Unable to open file handle PH for command '$cmd2': $!\n";
360 while (local $_ = <PH>) {
361 if (/\s+(\d+)%\s+packet\s+loss[\s,]/i) {
362 $update{"$host.PacketLoss"} = $1 || 0;
363 } elsif (my ($min,$avg,$max,$mdev) = $_ =~
364 /\s+([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+)\s+/) {
365 $update{"$host.AvgRTT"} = $avg || 0;
366 $update{"$host.MinRTT"} = $min || 0;
367 $update{"$host.MaxRTT"} = $max || 0;
368 $update{"$host.MDevRTT"} = $mdev || 0;
369 }
370 }
371 close(PH) || die "Unable to close file handle PH for command '$cmd2': $!\n";
372 }
373
374 return %update;
375 }
376
377
378
379 sub mem_proc_largest {
380 return if $opt{s};
381 my $cmd = select_cmd(qw(/bin/ps /usr/bin/ps));
382 return unless -f $cmd;
383 $cmd .= ' -eo vsize';
384
385 my %update = ();
386 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
387 while (local $_ = <PH>) {
388 if (/(\d+)/) {
389 my $kb = $1;
390 $update{LargestProc} = $kb if !defined $update{LargestProc} ||
391 (defined $update{LargestProc} && $kb > $update{LargestProc});
392 }
393 }
394 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
395 $update{LargestProc} *= 1024 if defined $update{LargestProc};
396
397 return %update;
398 }
399
400
401
402 sub proc_threads {
403 if ($opt{s}) {
404 my $procs = _snmp('.1.3.6.1.2.1.25.1.6.0'); # hrSystemProcesses
405 return unless defined($procs) && $procs =~ /^[0-9]+$/;
406 return ('Processes' => $procs, 'Threads' => 0, 'MultiThreadProcs' => 0);
407 }
408
409 return if $opt{s};
410 return unless ($^O eq 'linux' && `/bin/uname -r 2>&1` =~ /^2\.6\./) ||
411 ($^O eq 'solaris' && `/bin/uname -r 2>&1` =~ /^5\.9/);
412 my %update = ();
413 my $cmd = '/bin/ps -eo pid,nlwp';
414
415 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!";
416 while (local $_ = <PH>) {
417 if (my ($pid,$nlwp) = $_ =~ /^\s*(\d+)\s+(\d+)\s*$/) {
418 $update{Processes}++;
419 $update{Threads} += $nlwp;
420 $update{MultiThreadProcs}++ if $nlwp > 1;
421 }
422 }
423 close(PH) || die "Unable to close file handle PH for command '$cmd': $!";
424
425 return %update;
426 }
427
428
429
430 sub mail_exim_queue {
431 return if $opt{s};
432 my $spooldir = '/var/spool/exim/input';
433 return unless -d $spooldir && -x $spooldir && -r $spooldir;
434
435 local %mail::exim::queue::update = (Messages => 0);
436 require File::Find;
437 File::Find::find({wanted => sub {
438 my ($dev,$ino,$mode,$nlink,$uid,$gid);
439 (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
440 -f _ &&
441 /^.*-D\z/s &&
442 $mail::exim::queue::update{Messages}++;
443 }, no_chdir => 1}, $spooldir);
444 return %mail::exim::queue::update;
445 }
446
447
448 sub mail_sendmail_queue {
449 return if $opt{s};
450 my $spooldir = '/var/spool/mqueue';
451 return unless -d $spooldir && -x $spooldir && -r $spooldir;
452
453 local %mail::sendmail::queue::update = (Messages => 0);
454 require File::Find;
455 File::Find::find({wanted => sub {
456 my ($dev,$ino,$mode,$nlink,$uid,$gid);
457 (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
458 -f _ &&
459 /^Qf[a-zA-Z0-9]{14}\z/s &&
460 $mail::sendmail::queue::update{Messages}++;
461 }, no_chdir => 1}, $spooldir);
462 return %mail::sendmail::queue::update;
463 }
464
465
466
467 sub mail_postfix_queue {
468 return if $opt{s};
469 my @spooldirs = qw(
470 /var/spool/postfix/incoming
471 /var/spool/postfix/active
472 /var/spool/postfix/defer
473 /var/spool/postfix/deferred
474 );
475 for my $spooldir (@spooldirs) {
476 return unless -d $spooldir && -x $spooldir && -r $spooldir;
477 }
478
479 local %mail::postfix::queue::update = (Messages => 0);
480 require File::Find;
481 File::Find::find({wanted => sub {
482 my ($dev,$ino,$mode,$nlink,$uid,$gid);
483 (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
484 -f _ &&
485 $mail::postfix::queue::update{Messages}++;
486 }, no_chdir => 1}, @spooldirs);
487 return %mail::postfix::queue::update;
488 }
489
490
491
492 # DO NOT ENABLE THIS ONE YET
493 sub mail_queue {
494 return if $opt{s};
495 my $cmd = select_cmd(qw(/usr/bin/mailq /usr/sbin/mailq /usr/local/bin/mailq
496 /usr/local/sbin/mailq /bin/mailq /sbin/mailq
497 /usr/local/exim/bin/mailq /home/system/exim/bin/mailq));
498 return unless -f $cmd;
499
500 my %update = ();
501
502 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
503 while (local $_ = <PH>) {
504 # This needs to match a single message id = currently only exim friendly
505 if (/^\s*\S+\s+\S+\s+[a-z0-9]{6}-[a-z0-9]{6}-[a-z0-9]{2} </i) {
506 $update{Messages}++;
507 }
508 }
509 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
510 $update{Messages} = 0 if !defined($update{Messages});
511
512 return %update;
513 }
514
515
516
517 sub db_mysql {
518 return if $opt{s};
519 my %update = ();
520 return %update unless (defined DB_MYSQL_DSN && defined DB_MYSQL_USER);
521 my @cols = qw(
522 Questions
523 Connections
524 Com_select Com_insert Com_delete Com_replace Com_update Com_rollback Com_commit Com_set_option
525 Threads_cached Threads_connected Threads_created Threads_running
526 Bytes_sent Bytes_received
527 Innodb_row_lock_current_waits Innodb_row_lock_time_avg Innodb_row_lock_time_max Innodb_row_lock_waits
528 Select_scan Sort_scan
529 Created_tmp_disk_tables Created_tmp_files Created_tmp_tables
530
531 );
532
533 # my @GAUGE = qw(Key_blocks_not_flushed Key_blocks_unused Key_blocks_used
534 # Open_files Open_streams Open_tables Qcache_free_blocks Qcache_free_memory
535 # Qcache_queries_in_cache Qcache_total_blocks Slave_open_temp_tables
536 # Threads_cached Threads_connected Threads_running Uptime);
537
538 # my @DERIVE = qw(Aborted_clients Aborted_connects Binlog_cache_disk_use
539 # Binlog_cache_use Bytes_received Bytes_sent Com_admin_commands Com_alter_db
540 # Com_alter_table Com_analyze Com_backup_table Com_begin Com_change_db
541 # Com_change_master Com_check Com_checksum Com_commit Com_create_db
542 # Com_create_function Com_create_index Com_create_table Com_dealloc_sql
543 # Com_delete Com_delete_multi Com_do Com_drop_db Com_drop_function
544 # Com_drop_index Com_drop_table Com_drop_user Com_execute_sql Com_flush
545 # Com_grant Com_ha_close Com_ha_open Com_ha_read Com_help Com_insert
546 # Com_insert_select Com_kill Com_load Com_load_master_data Com_load_master_table
547 # Com_lock_tables Com_optimize Com_preload_keys Com_prepare_sql Com_purge
548 # Com_purge_before_date Com_rename_table Com_repair Com_replace Com_replace_select
549 # Com_reset Com_restore_table Com_revoke Com_revoke_all Com_rollback
550 # Com_savepoint Com_select Com_set_option Com_show_binlog_events
551 # Com_show_binlogs Com_show_charsets Com_show_collations Com_show_column_types
552 # Com_show_create_db Com_show_create_table Com_show_databases
553 # Com_show_errors Com_show_fields Com_show_grants Com_show_innodb_status
554 # Com_show_keys Com_show_logs Com_show_master_status Com_show_ndb_status
555 # Com_show_new_master Com_show_open_tables Com_show_privileges Com_show_processlist
556 # Com_show_slave_hosts Com_show_slave_status Com_show_status Com_show_storage_engines
557 # Com_show_tables Com_show_variables Com_show_warnings Com_slave_start
558 # Com_slave_stop Com_stmt_close Com_stmt_execute Com_stmt_prepare Com_stmt_reset
559 # Com_stmt_send_long_data Com_truncate Com_unlock_tables Com_update
560 # Com_update_multi Connections Created_tmp_disk_tables Created_tmp_files
561 # Created_tmp_tables Delayed_errors Delayed_insert_threads Delayed_writes
562 # Flush_commands Handler_commit Handler_delete Handler_discover Handler_read_first
563 # Handler_read_key Handler_read_next Handler_read_prev Handler_read_rnd
564 # Handler_read_rnd_next Handler_rollback Handler_update Handler_write
565 # Key_blocks_not_flushed Key_blocks_unused Key_blocks_used Key_read_requests
566 # Key_reads Key_write_requests Key_writes Max_used_connections Not_flushed_delayed_rows
567 # Open_files Open_streams Open_tables Opened_tables Qcache_free_blocks
568 # Qcache_free_memory Qcache_hits Qcache_inserts Qcache_lowmem_prunes Qcache_not_cached
569 # Qcache_queries_in_cache Qcache_total_blocks Questions Select_full_join
570 # Select_full_range_join Select_range Select_range_check Select_scan Slave_open_temp_tables
571 # Slave_retried_transactions Slow_launch_threads Slow_queries Sort_merge_passes
572 # Sort_range Sort_rows Sort_scan Ssl_accept_renegotiates Ssl_accepts
573 # Ssl_callback_cache_hits Ssl_client_connects Ssl_connect_renegotiates Ssl_ctx_verify_depth
574 # Ssl_ctx_verify_mode Ssl_default_timeout Ssl_finished_accepts Ssl_finished_connects
575 # Ssl_session_cache_hits Ssl_session_cache_misses Ssl_session_cache_overflows
576 # Ssl_session_cache_size Ssl_session_cache_timeouts Ssl_sessions_reused
577 # Ssl_used_session_cache_entries Ssl_verify_depth Ssl_verify_mode
578 # Table_locks_immediate Table_locks_waited Threads_cached Threads_connected
579 # Threads_created Threads_running);
580
581 eval {
582 require DBI;
583 my $dbh = DBI->connect(DB_MYSQL_DSN,DB_MYSQL_USER,DB_MYSQL_PASS);
584 #my $sth = $dbh->prepare('SHOW GLOBAL STATUS');
585 my $sth = $dbh->prepare('SHOW /*!50002 GLOBAL */ STATUS');
586 $sth->execute();
587
588 while (my @ary = $sth->fetchrow_array()) {
589 my ($k,$v) = @ary;
590 next unless grep { $k eq $_ } @cols;
591
592 if ($k eq 'Questions') {
593 $update{"activity.$k"} = $v;
594 } elsif ($k =~ /^Com_/) {
595 $update{"activity.com.$k"} = $v;
596 } elsif ($k =~ /_scan$/) {
597 $update{"activity.scan.$k"} = $v;
598
599 } elsif ($k eq 'Connections') {
600 $update{"connections.$k"} = $v;
601
602 } elsif ($k =~ /^Threads_/) {
603 $update{"threads.$k"} = $v;
604 } elsif ($k =~ /^Bytes_/) {
605 $update{"traffic.$k"} = $v;
606
607 } elsif ($k =~ /^Created_tmp_/) {
608 $k =~ s/^Created_tmp_//;
609 $update{"created_tmp.$k"} = $v;
610 } elsif ($k =~ /^Innodb_row_lock_/) {
611 $k =~ s/^Innodb_row_lock_//;
612 $update{"innodb.row_lock.$k"} = $v;
613 }
614 }
615 $sth->finish();
616 $dbh->disconnect();
617 };
618
619 return %update;
620 }
621
622
623
624 sub db_mysql_replication {
625 return if $opt{s};
626 my %update = ();
627 return %update unless (defined DB_MYSQL_DSN && defined DB_MYSQL_USER);
628
629 eval {
630 require DBI;
631 my $dbh = DBI->connect(DB_MYSQL_DSN,DB_MYSQL_USER,DB_MYSQL_PASS);
632 my $sth = $dbh->prepare('SHOW SLAVE STATUS');
633 $sth->execute();
634 my $row = $sth->fetchrow_hashref;
635 $sth->finish();
636 $dbh->disconnect();
637 $update{SecondsBehind} = $row->{Seconds_Behind_Master} || 0;
638 };
639
640 return %update;
641 }
642
643
644
645 sub misc_users {
646 if ($opt{s}) {
647 my $users = _snmp('.1.3.6.1.2.1.25.1.5.0'); # hrSystemNumUsers
648 return unless defined($users) && $users =~ /^[0-9]+$/;
649 return ('Users' => $users, 'Unique' => 0);
650 }
651
652 return if $opt{s};
653 my $cmd = select_cmd(qw(/usr/bin/who /bin/who /usr/bin/w /bin/w));
654 return unless -f $cmd;
655 my %update = ();
656
657 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
658 my %users = ();
659 while (local $_ = <PH>) {
660 next if /^\s*USERS\s*TTY/;
661 $users{(split(/\s+/,$_))[0]}++;
662 $update{Users}++;
663 }
664 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
665 $update{Unique} = keys %users if keys %users;
666
667 unless (keys %update) {
668 $cmd = -f '/usr/bin/uptime' ? '/usr/bin/uptime' : '/bin/uptime';
669 if (my ($users) = `$cmd` =~ /,\s*(\d+)\s*users?\s*,/i) {
670 $update{Users} = $1;
671 }
672 }
673
674 $update{Users} ||= 0;
675 $update{Unique} ||= 0;
676
677 return %update;
678 }
679
680
681
682 sub misc_uptime {
683 if ($opt{s}) {
684 my $ticks = _snmp('.1.3.6.1.2.1.25.1.1.0'); # hrSystemUptime
685 return unless defined($ticks) && $ticks =~ /^[0-9]+$/;
686 return ('DaysUp' => $ticks/100/60/60/24);
687 }
688
689 my $cmd = select_cmd(qw(/usr/bin/uptime /bin/uptime));
690 return unless -f $cmd;
691 my %update = ();
692
693 if (my ($str) = `$cmd` =~ /\s*up\s*(.+?)\s*,\s*\d+\s*users?/) {
694 my $days = 0;
695 if (my ($nuke,$num) = $str =~ /(\s*(\d+)\s*days?,?\s*)/) {
696 $str =~ s/$nuke//;
697 $days += $num;
698 }
699 if (my ($nuke,$mins) = $str =~ /(\s*(\d+)\s*mins?,?\s*)/) {
700 $str =~ s/$nuke//;
701 $days += ($mins / (60*24));
702 }
703 if (my ($nuke,$hours) = $str =~ /(\s*(\d+)\s*(hour|hr)s?,?\s*)/) {
704 $str =~ s/$nuke//;
705 $days += ($hours / 24);
706 }
707 if (my ($hours,$mins) = $str =~ /\s*(\d+):(\d+)\s*,?/) {
708 $days += ($mins / (60*24));
709 $days += ($hours / 24);
710 }
711 $update{DaysUp} = $days;
712 }
713
714 return %update;
715 }
716
717
718
719 sub cpu_temp {
720 return if $opt{s};
721 my $cmd = '/usr/bin/sensors';
722 return unless -f $cmd;
723 my %update = ();
724
725 open(PH,'-|',"$cmd 2>&1") || die "Unable to open file handle PH for command '$cmd': $!\n";
726 while (local $_ = <PH>) {
727 if (my ($k,$v) = $_ =~ /^([^:]*\b(?:CPU|temp)\d*\b.*?):\s*\S*?([\d\.]+)\S*\s*/i) {
728 $k =~ s/\W//g; $k =~ s/Temp$//i;
729 $update{$k} = $v;
730 } elsif (/(no sensors found|kernel driver|sensors-detect|error|warning)/i
731 && !$opt{q}) {
732 warn "Warning [cpu_temp]: $_";
733 }
734 }
735 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
736
737 return %update;
738 }
739
740
741
742 sub apache_logs {
743 return if $opt{s};
744 my $dir = '/var/log/httpd';
745 return unless -d $dir;
746 my %update = ();
747
748 if (-d $dir) {
749 opendir(DH,$dir) || die "Unable to open file handle for directory '$dir': $!\n";
750 my @files = grep(!/^\./,readdir(DH));
751 closedir(DH) || die "Unable to close file handle for directory '$dir': $!\n";
752 for (@files) {
753 next if /\.(\d+|gz|bz2|Z|zip|old|bak|pid|backup)$/i || /[_\.\-]pid$/;
754 my $file = "$dir/$_";
755 next unless -f $file;
756 s/[\.\-]/_/g;
757 $update{$_} = (stat($file))[7];
758 }
759 }
760
761 return %update;
762 }
763
764
765
766 sub apache_status {
767 return if $opt{s};
768 my @data = ();
769 my %update = ();
770
771 my $timeout = 5;
772 my $url = 'http://localhost/server-status?auto';
773 my %keys = (W => 'Write', G => 'GraceClose', D => 'DNS', S => 'Starting',
774 L => 'Logging', R => 'Read', K => 'Keepalive', C => 'Closing',
775 I => 'Idle', '_' => 'Waiting');
776
777
778 eval "use LWP::UserAgent";
779 unless ($@) {
780 eval {
781 my $ua = LWP::UserAgent->new(
782 agent => "$0 version $VERSION ".'($Id)',
783 timeout => $timeout);
784 $ua->env_proxy;
785 $ua->max_size(1024*250);
786 my $response = $ua->get($url);
787 if ($response->is_success) {
788 @data = split(/\n+|\r+/,$response->content);
789 } elsif (!$opt{q}) {
790 warn "Warning [apache_status]: failed to get $url; ". $response->status_line ."\n";
791 }
792 };
793 }
794 if ($@) {
795 @data = basic_http('GET',$url,$timeout);
796 }
797
798 for (@data) {
799 my ($k,$v) = $_ =~ /^\s*(.+?):\s+(.+?)\s*$/;
800 $k = '' unless defined $k;
801 $v = '' unless defined $v;
802 $k =~ s/\s+//g; #$k = lc($k);
803 next unless $k;
804 if ($k eq 'Scoreboard') {
805 my %x; $x{$_}++ for split(//,$v);
806 for (keys %keys) {
807 $update{"scoreboard.$keys{$_}"} =
808 defined $x{$_} ? $x{$_} : 0;
809 }
810 } else {
811 $update{$k} = $v;
812 }
813 }
814
815 $update{ReqPerSec} = int($update{TotalAccesses})
816 if defined $update{TotalAccesses};
817 $update{BytesPerSec} = int($update{TotalkBytes} * 1024)
818 if defined $update{TotalkBytes};
819
820 return %update;
821 }
822
823
824
825 sub _darwin_cpu_utilisation {
826 my $output = qx{/usr/bin/sar 4 1};
827 my %rv = ();
828 if ($output =~ m/Average:\s+(\d+)\s+(\d+)\s+(\d+)/) {
829 %rv = (
830 User => $1,
831 System => $2,
832 Idle => $3,
833 IO_Wait => 0, # at the time of writing, sar doesn't provide this metric
834 );
835 }
836 return %rv;
837 }
838
839
840
841 sub cpu_utilisation {
842 my %update = ();
843 if ($opt{s}) {
844 $update{'User'} = _snmp('.1.3.6.1.4.1.2021.11.9.0');
845 $update{'System'} = _snmp('.1.3.6.1.4.1.2021.11.10.0');
846 $update{'Idle'} = _snmp('.1.3.6.1.4.1.2021.11.11.0');
847 #$update{'IO_Wait'} = 100 - $update{'User'} - $update{'System'} - $update{'Idle'};
848 $update{'IO_Wait'} = 0;
849
850 # Try querying the Windows thingie instead
851 unless (grep(/^[0-9\.]{1,3}$/, values(%update)) == 4) {
852 # hrProcessorLoad
853 # .1.3.6.1.2.1.25.3.3.1.2.1 - CPU 1
854 # .1.3.6.1.2.1.25.3.3.1.2.2 - CPU 2 ...
855 my $total; my $cpu;
856 for ($cpu = 1; $cpu <= 16; $cpu++) {
857 my $load = _snmp(".1.3.6.1.2.1.25.3.3.1.2.$cpu");
858 if (defined($load) && !ref($load) && $load =~ /^[0-9\.]{1,3}$/) {
859 $total += $load;
860 } else {
861 last;
862 }
863 }
864 return unless $total && $cpu-1;
865 %update = ('User' => int($total / $cpu-1), 'System' => 0, 'Idle' => 0, 'IO_Wait' => 0);
866 }
867 return %update;
868 }
869
870 if ($^O eq 'darwin') {
871 return _darwin_cpu_utilisation();
872 }
873
874 my $cmd = '/usr/bin/vmstat';
875 return unless -f $cmd;
876 %update = _parse_vmstat("$cmd 1 2");
877 my %labels = (wa => 'IO_Wait', id => 'Idle', sy => 'System', us => 'User');
878
879 $update{$_} ||= 0 for keys %labels;
880 return ( map {( $labels{$_} || $_ => $update{$_} )} keys %labels );
881 }
882
883
884
885 sub hw_irq_interrupts {
886 return if $opt{s};
887
888 my @update;
889 if (open(FH,'<','/proc/interrupts')) {
890 local $_ = <FH>;
891 return unless /^\s+(CPU[0-9]+.*)/;
892 my @cpus = split(/\s+/,$1);
893 $_ = lc($_) for @cpus;
894
895 my %data;
896 while (local $_ = <FH>) {
897 if (/^\s*([0-9]{1,2}):\s+([\s0-9]+)\s+(\S+)\s+(.+?)\s*$/) {
898 my ($irq,$ints,$path,$src) = ($1,$2,$3,$4);
899 my @ints = split(/\s+/,$ints);
900 for (my $i = 0; $i <= @cpus; $i++) {
901 my $cpu = $cpus[$i];
902 my $int = $ints[$i];
903 next unless defined $cpu && defined $int;
904 $data{$cpu}->{"$cpu.irq$irq"} = $int;
905 }
906 }
907 }
908
909 for my $cpu (keys %data) {
910 if (grep(/[1-9]/,values %{$data{$cpu}})) {
911 push @update, %{$data{$cpu}};
912 }
913 }
914
915 close(FH) || warn "Unable to close file handle FH for file '/proc/interrupts': $!";
916 }
917
918 return @update;
919 }
920
921
922
923 sub cpu_interrupts {
924 return if $opt{s};
925 my $cmd = '/usr/bin/vmstat';
926 return unless -f $cmd;
927
928 my %update = _parse_vmstat("$cmd 1 2");
929 my %labels = (in => 'Interrupts');
930 return unless defined $update{in};
931
932 $update{$_} ||= 0 for keys %labels;
933 return ( map {( $labels{$_} || $_ => $update{$_} )} keys %labels );
934 }
935
936
937
938 sub mem_swap_activity {
939 return if $opt{s};
940 my $cmd = '/usr/bin/vmstat';
941 return unless -f $cmd;
942
943 my %update = _parse_vmstat("$cmd 1 2");
944 my %labels = (si => 'Swap_In', so => 'Swap_Out');
945 return unless defined $update{si} && defined $update{so};
946
947 $update{$_} ||= 0 for keys %labels;
948 return ( map {( $labels{$_} || $_ => $update{$_} )} keys %labels );
949 }
950
951
952
953 sub _parse_vmstat {
954 my $cmd = shift;
955 my %update;
956 my @keys;
957
958 if (exists $update_cache{vmstat}) {
959 %update = %{$update_cache{vmstat}};
960 } else {
961 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
962 while (local $_ = <PH>) {
963 s/^\s+|\s+$//g;
964 if (/\s+\d+\s+\d+\s+\d+\s+/ && @keys) {
965 @update{@keys} = split(/\s+/,$_);
966 } else { @keys = split(/\s+/,$_); }
967 }
968 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
969 $update_cache{vmstat} = \%update;
970 }
971
972 return %update;
973 }
974
975
976
977 sub _parse_ipmitool_sensor {
978 my $cmd = shift;
979 my %update;
980 my @keys;
981
982 if (exists $update_cache{ipmitool_sensor}) {
983 %update = %{$update_cache{ipmitool_sensor}};
984 } else {
985 if ((-e '/dev/ipmi0' || -e '/dev/ipmi/0') && open(PH,'-|',$cmd)) {
986 while (local $_ = <PH>) {
987 chomp; s/(^\s+|\s+$)//g;
988 my ($key,@ary) = split(/\s*\|\s*/,$_);
989 $key =~ s/[^a-zA-Z0-9_]//g;
990 $update{$key} = \@ary;
991 }
992 close(PH);
993 $update_cache{ipmitool_sensor} = \%update;
994 }
995 }
996
997 return %update;
998 }
999
1000
1001
1002 sub misc_ipmi_temp {
1003 return if $opt{s};
1004 my $cmd = select_cmd(qw(/usr/bin/ipmitool));
1005 return unless -f $cmd;
1006
1007 my %update = ();
1008 my %data = _parse_ipmitool_sensor("$cmd sensor");
1009 for (grep(/temp/i,keys %data)) {
1010 $update{$_} = $data{$_}->[0]
1011 if $data{$_}->[0] =~ /^[0-9\.]+$/;
1012 }
1013 return unless keys %update;
1014
1015 return %update;;
1016 }
1017
1018
1019
1020 sub hdd_io {
1021 return if $opt{s};
1022 my $cmd = select_cmd(qw(/usr/bin/iostat /usr/sbin/iostat));
1023 return unless -f $cmd;
1024 return unless $^O eq 'linux';
1025 $cmd .= ' -k';
1026
1027 my %update = ();
1028
1029 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
1030 while (local $_ = <PH>) {
1031 if (my ($dev,$r,$w) = $_ =~ /^([\/\w\d]+)\s+\S+\s+\S+\s+\S+\s+(\d+)\s+(\d+)$/) {
1032 $dev =~ s/[^\w\d]+/_/g;
1033 $update{"$dev.Read"} = $r*1024;
1034 $update{"$dev.Write"} = $w*1024;
1035 }
1036 }
1037 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
1038
1039 return %update;
1040 }
1041
1042
1043
1044 sub mem_usage {
1045 return if $opt{s};
1046 my %update = ();
1047 my $cmd = select_cmd(qw(/usr/bin/free /bin/free));
1048 my @keys = ();
1049
1050 if ($^O eq 'linux' && -f $cmd && -x $cmd) {
1051 $cmd .= ' -b';
1052 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
1053 while (local $_ = <PH>) {
1054 if (@keys && /^Mem:\s*(\d+.+)\s*$/i) {
1055 my @values = split(/\s+/,$1);
1056 for (my $i = 0; $i < @values; $i++) {
1057 $update{ucfirst($keys[$i])} = $values[$i];
1058 }
1059 $update{Used} = $update{Used} - $update{Buffers} - $update{Cached};
1060
1061 } elsif (@keys && /^Swap:\s*(\d+.+)\s*$/i) {
1062 my @values = split(/\s+/,$1);
1063 for (my $i = 0; $i < @values; $i++) {
1064 $update{"swap.".ucfirst($keys[$i])} = $values[$i];
1065 }
1066
1067 } elsif (!@keys && /^\s*([\w\s]+)\s*$/) {
1068 @keys = split(/\s+/,$1);
1069 }
1070 }
1071 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
1072
1073 } elsif ($^O eq 'darwin' && -x '/usr/sbin/sysctl') {
1074 my $swap = qx{/usr/sbin/sysctl vm.swapusage};
1075 if ($swap =~ m/total = (.+)M used = (.+)M free = (.+)M/) {
1076 $update{"swap.Total"} = $1*1024*1024;
1077 $update{"swap.Used"} = $2*1024*1024;
1078 $update{"swap.Free"} = $3*1024*1024;
1079 }
1080
1081 } else {
1082 eval "use Sys::MemInfo qw(totalmem freemem)";
1083 die "Please install Sys::MemInfo so that I can get memory information.\n" if $@;
1084 @update{qw(Total Free)} = (totalmem(),freemem());
1085 }
1086
1087 return %update;
1088 }
1089
1090
1091
1092 sub hdd_temp {
1093 return if $opt{s};
1094 my $cmd = select_cmd(qw(/usr/sbin/hddtemp /usr/bin/hddtemp));
1095 return unless -f $cmd;
1096
1097 my @devs = ();
1098 for my $dev (glob('/dev/hd?'),glob('/dev/sd?')) {
1099 if ($dev =~ /^(\/dev\/\w{3})$/i) {
1100 push @devs, $1;
1101 }
1102 }
1103
1104 $cmd .= " -q @devs 2>&1";
1105 my %update = ();
1106 return %update unless @devs;
1107
1108 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
1109 while (local $_ = <PH>) {
1110 if (my ($dev,$temp) = $_ =~ m,^/dev/([a-z]+):\s+.+?:\s+(\d+)..?C,) {
1111 $update{$dev} = $temp;
1112 } elsif (!/^\s*$/ && !$opt{q}) {
1113 warn "Warning [hdd_temp]: $_";
1114 }
1115 }
1116 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
1117
1118 return %update;
1119 }
1120
1121
1122
1123 sub hdd_capacity {
1124 if ($opt{s}) {
1125 return;
1126
1127 my $snmp = _snmp('.1.3.6.1.4.1.2021.9.1'); # dskTable
1128 return unless defined($snmp) && ref($snmp) eq 'HASH';
1129
1130 my %disks;
1131 #.1.3.6.1.4.1.2021.9.1.1.1 = INTEGER: 1
1132 #.1.3.6.1.4.1.2021.9.1.2.1 = STRING: /
1133 #.1.3.6.1.4.1.2021.9.1.3.1 = STRING: /dev/sda1
1134 #.1.3.6.1.4.1.2021.9.1.4.1 = INTEGER: 100000
1135 #.1.3.6.1.4.1.2021.9.1.5.1 = INTEGER: -1
1136 #.1.3.6.1.4.1.2021.9.1.6.1 = INTEGER: 7850996
1137 #.1.3.6.1.4.1.2021.9.1.7.1 = INTEGER: 3153808
1138 #.1.3.6.1.4.1.2021.9.1.8.1 = INTEGER: 4298376
1139 #.1.3.6.1.4.1.2021.9.1.9.1 = INTEGER: 58
1140 #.1.3.6.1.4.1.2021.9.1.10.1 = INTEGER: 16
1141 #.1.3.6.1.4.1.2021.9.1.100.1 = INTEGER: 0
1142 #.1.3.6.1.4.1.2021.9.1.101.1 = STRING:
1143
1144 #'UCD-SNMP-MIB::dskMinimum.1' => '100000',
1145 #'UCD-SNMP-MIB::dskErrorMsg.1' => '',
1146 #'UCD-SNMP-MIB::dskIndex.1' => '1',
1147 #'UCD-SNMP-MIB::dskPath.1' => '/',
1148 #'UCD-SNMP-MIB::dskPercentNode.1' => '16',
1149 #'UCD-SNMP-MIB::dskErrorFlag.1' => '0',
1150 #'UCD-SNMP-MIB::dskAvail.1' => '3153784',
1151 #'#UCD-SNMP-MIB::dskPercent.1' => '58',
1152 #'UCD-SNMP-MIB::dskMinPercent.1' => '-1',
1153 #'UCD-SNMP-MIB::dskDevice.1' => '/dev/sda1',
1154 #'UCD-SNMP-MIB::dskUsed.1' => '4298400',
1155 #'UCD-SNMP-MIB::dskTotal.1' => '7850996'
1156
1157 #use Data::Dumper;
1158 #warn Dumper($snmp);
1159 return;
1160 }
1161
1162 my $cmd = select_cmd(qw(/bin/df /usr/bin/df));
1163 return unless -f $cmd;
1164
1165 if ($^O eq 'linux') { $cmd .= ' -P -x iso9660 -x nfs -x smbfs'; }
1166 elsif ($^O eq 'solaris') { $cmd .= ' -lk -F ufs'; }
1167 elsif ($^O eq 'darwin') { $cmd .= ' -P -T hfs,ufs'; }
1168 else { $cmd .= ' -P'; }
1169
1170 my %update = ();
1171 my %variants = (
1172 '' => '',
1173 'inodes.' => ' -i ',
1174 );
1175
1176 for my $variant (keys %variants) {
1177 my $variant_cmd = "$cmd $variants{$variant}";
1178 my @data = split(/\n/, `$variant_cmd`);
1179 shift @data;
1180
1181 my @cols = qw(fs blocks used avail capacity mount unknown);
1182 for (@data) {
1183 my %data = ();
1184 @data{@cols} = split(/\s+/,$_);
1185 if ($^O eq 'darwin' || defined $data{unknown}) {
1186 @data{@cols} = $_ =~ /^(.+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%?\s+(.+)\s*$/;
1187 }
1188
1189 next if ($data{fs} eq 'none' || $data{mount} =~ m#^/dev/#);
1190 $data{capacity} =~ s/\%//;
1191 (my $ds = $data{mount}) =~ s/[^a-z0-9]/_/ig; $ds =~ s/__+/_/g;
1192
1193 # McAfee SCM 4.2 bodge-o-rama fix work around
1194 next if $ds =~ /^_var_jails_d_spam_/;
1195
1196 $update{"${variant}$ds"} = $data{capacity};
1197 }
1198 }
1199
1200 return %update;
1201 }
1202
1203
1204
1205 sub misc_entropy {
1206 return if $opt{s};
1207 my $file = '/proc/sys/kernel/random/entropy_avail';
1208 return unless -f $file;
1209 my %update = ();
1210
1211 open(FH,'<',$file) || die "Unable to open '$file': $!\n";
1212 chomp($update{entropy_avail} = <FH>);
1213 close(FH) || die "Unable to close '$file': $!\n";
1214
1215 return %update;
1216 }
1217
1218
1219
1220 sub net_traffic {
1221 return if $opt{s};
1222 return unless -f '/proc/net/dev';
1223 my @keys = ();
1224 my %update = ();
1225
1226 open(FH,'<','/proc/net/dev') || die "Unable to open '/proc/net/dev': $!\n";
1227 while (local $_ = <FH>) {
1228 s/^\s+|\s+$//g;
1229 if ((my ($dev,$data) = $_ =~ /^(.+?):\s*(\d+.+)\s*$/) && @keys) {
1230 my @values = split(/\s+/,$data);
1231 for (my $i = 0; $i < @keys; $i++) {
1232 if ($keys[$i] eq 'TXbytes') {
1233 $update{"$dev.Transmit"} = $values[$i];
1234 } elsif ($keys[$i] eq 'RXbytes') {
1235 $update{"$dev.Receive"} = $values[$i];
1236 }
1237 #$update{"$dev.$keys[$i]"} = $values[$i];
1238 }
1239 } else {
1240 my ($rx,$tx) = (split(/\s*\|\s*/,$_))[1,2];
1241 @keys = (map({"RX$_"} split(/\s+/,$rx)), map{"TX$_"} split(/\s+/,$tx));
1242 }
1243 }
1244 close(FH) || die "Unable to close '/proc/net/dev': $!\n";
1245
1246 return %update;
1247 }
1248
1249
1250
1251 sub proc_state {
1252 return if $opt{s};
1253 my $cmd = select_cmd(qw(/bin/ps /usr/bin/ps));
1254 my %update = ();
1255 my %keys = ();
1256
1257 if (-f $cmd && -x $cmd) {
1258 if ($^O eq 'freebsd' || $^O eq 'darwin') {
1259 $cmd .= ' axo pid,state';
1260 # %keys = (D => 'IO_Wait', R => 'Run', S => 'Sleep', T => 'Stopped',
1261 # I => 'Idle', L => 'Lock_Wait', Z => 'Zombie', W => 'Idle_Thread');
1262 %keys = (D => 'IO_Wait', R => 'Run', S => 'Sleep', T => 'Stopped',
1263 W => 'Paging', Z => 'Zombie', I => 'Sleep');
1264 } else {#} elsif ($^O =~ /^(linux|solaris)$/)
1265 $cmd .= ' -eo pid,s';
1266 %keys = (D => 'IO_Wait', R => 'Run', S => 'Sleep', T => 'Stopped',
1267 W => 'Paging', X => 'Dead', Z => 'Zombie');
1268 }
1269
1270 my $known_keys = join('',keys %keys);
1271 open(PH,'-|',$cmd) || die "Unable to open file handle PH for command '$cmd': $!\n";
1272 while (local $_ = <PH>) {
1273 if (my ($pid,$state) = $_ =~ /^\s*(\d+)\s+(\S+)\s*$/) {
1274 $state =~ s/[^$known_keys]//g;
1275 $update{$keys{$state}||$state}++ if $state;
1276 }
1277 }
1278 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
1279 $update{$_} ||= 0 for values %keys;
1280
1281 } else {
1282 eval "use Proc::ProcessTable";
1283 die "Please install /bin/ps or Proc::ProcessTable\n" if $@;
1284 my $p = new Proc::ProcessTable("cache_ttys" => 1 );
1285 for (@{$p->table}) {
1286 $update{$_->{state}}++;
1287 }
1288 }
1289
1290 return %update;
1291 }
1292
1293
1294
1295 sub cpu_loadavg {
1296 my %update = ();
1297 if ($opt{s}) {
1298 $update{'1min'} = _snmp('.1.3.6.1.4.1.2021.10.1.3.1');
1299 $update{'5min'} = _snmp('.1.3.6.1.4.1.2021.10.1.3.2');
1300 $update{'15min'} = _snmp('.1.3.6.1.4.1.2021.10.1.3.3');
1301 return %update;
1302 }
1303
1304 my @data = ();
1305 if (-f '/proc/loadavg') {
1306 open(FH,'<','/proc/loadavg') || die "Unable to open file handle FH for file '/proc/loadavg': $!\n";
1307 my $str = <FH>;
1308 close(FH) || die "Unable to close file handle FH for file '/proc/loadavg': $!\n";
1309 @data = split(/\s+/,$str);
1310
1311 } else {
1312 my $cmd = -f '/usr/bin/uptime' ? '/usr/bin/uptime' : '/bin/uptime';
1313 @data = `$cmd` =~ /[\s:]+([\d\.]+)[,\s]+([\d\.]+)[,\s]+([\d\.]+)\s*$/;
1314 }
1315
1316 %update = (
1317 "1min" => $data[0],
1318 "5min" => $data[1],
1319 "15min" => $data[2],
1320 );
1321
1322 return %update;
1323 }
1324
1325
1326
1327 sub _parse_netstat {
1328 my $cmd = shift;
1329 my $update;
1330 my @keys = qw(local_ip local_port remote_ip remote_port);
1331
1332 if (exists $update_cache{netstat}) {
1333 $update = $update_cache{netstat};
1334 } else {
1335 open(PH,'-|',$cmd) || die "Unable to open file handle for command '$cmd': $!\n";
1336 while (local $_ = <PH>) {
1337 my %line;
1338 if (@line{qw(proto data state)} = $_ =~ /^(tcp[46]?|udp[46]?|raw)\s+(.+)\s+([A-Z_]+)\s*$/) {
1339 @line{@keys} = $line{data} =~ /(?:^|[\s\b])([:abcdef0-9\.]+):(\d{1,5})(?:[\s\b]|$)/g;
1340 push @{$update}, \%line;
1341 }
1342 }
1343 close(PH) || die "Unable to close file handle PH for command '$cmd': $!\n";
1344 $update_cache{netstat} = $update;
1345 }
1346
1347 return $update;
1348 }
1349
1350
1351
1352 sub net_connections_ports {
1353 return if $opt{s};
1354 my $cmd = select_cmd(qw(/bin/netstat /usr/bin/netstat /usr/sbin/netstat));
1355 return unless -f $cmd;
1356 $cmd .= ' -na 2>&1';
1357
1358 my %update = ();
1359 my %listening_ports;
1360 for (@{_parse_netstat($cmd)}) {
1361 if ($_->{state} =~ /listen/i && defined $_->{local_port}) {
1362 $listening_ports{"$_->{proto}:$_->{local_port}"} = 1;
1363 $update{"$_->{proto}_$_->{local_port}"} = 0;
1364 }
1365 }
1366 for (@{_parse_netstat($cmd)}) {
1367 next if !defined $_->{state} || !defined $_->{remote_port};
1368 $update{"$_->{proto}_$_->{remote_port}"}++ if exists $listening_ports{"$_->{proto}:$_->{remote_port}"};
1369 }
1370
1371 return %update;
1372 }
1373
1374
1375
1376 sub net_connections {
1377 return if $opt{s};
1378 my $cmd = select_cmd(qw(/bin/netstat /usr/bin/netstat /usr/sbin/netstat));
1379 return unless -f $cmd;
1380 $cmd .= ' -na 2>&1';
1381
1382 my %update = ();
1383 for (@{_parse_netstat($cmd)}) {
1384 $update{$_->{state}}++ if defined $_->{state};
1385 }
1386
1387 return %update;
1388 }
1389
1390
1391
1392 sub proc_filehandles {
1393 return if $opt{s};
1394 return unless -f '/proc/sys/fs/file-nr';
1395 my %update = ();
1396
1397 open(FH,'<','/proc/sys/fs/file-nr') || die "Unable to open file handle FH for file '/proc/sys/fs/file-nr': $!\n";
1398 my $str = <FH>;
1399 close(FH) || die "Unable to close file handle FH for file '/proc/sys/fs/file-nr': $!\n";
1400 @update{qw(Allocated Free Maximum)} = split(/\s+/,$str);
1401 $update{Used} = $update{Allocated} - $update{Free};
1402
1403 return %update;
1404 }
1405
1406
1407
1408
1409
1410

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26