/[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

Annotation of /bin/rrd-client.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Thu Jul 16 18:48:19 2009 UTC (14 years, 9 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 dpavlin 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