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