1 #!/usr/bin/perl -w
2 # check_sybase
3 # A nagios plugin that connects to a Sybase database and checks free space.
4 #
5 # Copyright 2004 Simon Bellwood, NetMan Network Management and IT Services GmbH
6 # Portions Copyright 2001 Michael Peppler.
7 # License: GPL
8 #
9 # Bugs and feedback to simon.bellwood@nospam.net-man.at
10 # Latest version available from:
11 # http://www.net-man.at/software/check_sybase-LATEST.zip
12 #
13 # Revision history:
14 # 0.1 01-OCT-2004 Initial version.
15 # 0.2 08-NOV-2004 Initial release.
16 # 0.3 13-JAN-2004 Fixed lib path, improved timeouts.
17 # 0.4 26-JAN-2004 Added loginTimeout.
18 my $VERSION = "0.4";
20 use strict;
21 use DBI;
22 use Getopt::Long;
23 use lib qw( /usr/lib/nagios/plugins/ /usr/local/nagios/libexec/ );
24 use utils qw(%ERRORS &print_revision &support &usage $TIMEOUT);
27 my $PROGNAME = "check_sybase";
28 my $DEFAULT_CHECKTYPE = "FREESPACE";
29 my $DEFAULT_WARNING = "25";
30 my $DEFAULT_CRITICAL = "10";
31 my $DEFAULT_TIMEOUT = "30";
33 my ($user, $pass, $dbsvr, $dbname, $config, $checktype, $warn, $crit, $timeout,
34 $help, $version);
36 my $options_okay = GetOptions(
37 "U|user=s" => \$user,
38 "P|pass:s" => \$pass, # ":" means optional
39 "S|dbsvr=s" => \$dbsvr,
40 "D|dbname=s" => \$dbname,
41 "config=s" => \$config,
42 "checktype=s" => \$checktype,
43 "w|warning=i" => \$warn,
44 "c|critical=i" => \$crit,
45 "t|timeout=i" => \$timeout,
46 "h|help" => \$help,
47 "V|version" => \$version
48 );
51 if (! $options_okay) # Bad option passed
52 {
53 &help;
54 &nunk("Bad command line option passed!");
55 }
57 # Use defaults, if needed
58 $warn = $warn || $DEFAULT_WARNING;
59 $crit = $crit || $DEFAULT_CRITICAL;
60 $checktype = $checktype || $DEFAULT_CHECKTYPE;
61 $timeout = $timeout || $TIMEOUT || $DEFAULT_TIMEOUT;
63 if ($help)
64 {
65 &help;
66 &nok;
67 }
69 if ($version)
70 {
71 print_revision($PROGNAME,"\$Revision$VERSION \$");
72 &nok;
73 }
75 if ($config) # Read any of "user", "pass", "dbsvr", "dbname" from config file
76 {
77 &read_config;
78 }
80 # Some more descriptive syntax checks
81 my $syntax_error;
82 $syntax_error .= "No dbsvr given! " unless $dbsvr;
83 $syntax_error .= "No dbname given! " unless $dbname;
84 $syntax_error .= "No user given! " unless $user;
85 $syntax_error .= "Bad checktype given!"
86 unless $checktype =~ m/^CONNECT|FREESPACE$/;
87 &nunk($syntax_error) if $syntax_error;
90 # Just in case of problems, let's not hang Nagios
91 $SIG{'ALRM'} = sub {
92 &nunk("Timeout: no response from dbsvr $dbsvr within $timeout seconds");
93 };
94 alarm($timeout);
97 # Decide on what we are checking
98 if ($checktype eq "CONNECT")
99 {
100 &connect;
101 }
102 elsif ($checktype eq "FREESPACE")
103 {
104 &check_space;
105 }
107 my $dbh;
108 my $is_connected;
109 sub connect
110 {
111 $dbh = DBI->connect("dbi:Sybase:server=$dbsvr;database=$dbname;".
112 "timeout=$timeout,loginTimeout=$timeout", $user, $pass)
113 or &ncrit("Could not connect to '$dbname' on '$dbsvr'");
115 # Report success for a check of type CONNECT
116 &nok("Connect okay") if $checktype ne "FREESPACE";
117 }
119 sub disconnect
120 {
121 $dbh->disconnect if $is_connected;
122 $is_connected = 0;
123 }
125 sub check_space
126 {
127 &connect;
129 # Most of this sub based on Michael Peppler's check-space.pl
131 $dbh->{syb_do_proc_status} = 1;
133 my $dbinfo;
135 # First check space in the database
136 my $sth = $dbh->prepare("sp_spaceused")
137 or &nunk("Failed to call sp_spaceused on '$dbsvr'");
138 $sth->execute
139 or &nunk("Failed to call sp_spaceused on '$dbsvr'");
140 do {
141 while(my $d = $sth->fetch)
142 {
143 if($d->[0] =~ /$dbname/)
144 {
145 # Grab "database_size"
146 $d->[1] =~ s/[^\d.]//g;
147 $dbinfo->{size} = $d->[1];
148 }
149 else
150 {
151 foreach (@$d)
152 {
153 s/\D//g;
154 }
156 # Grab "reserved", "data", "index"
157 $dbinfo->{reserved} = $d->[0] / 1024;
158 $dbinfo->{data} = $d->[1] / 1024;
159 $dbinfo->{index} = $d->[2] / 1024;
160 }
161 }
162 } while($sth->{syb_more_results});
164 # Get the actual device usage from sp_helpdb to get the free log space
165 $sth = $dbh->prepare("sp_helpdb $dbname")
166 or &nunk("Failed to call sp_helpdb $dbname on '$dbsvr'");
167 $sth->execute
168 or &nunk("Failed to call sp_helpdb $dbname on '$dbsvr'");
169 do {
170 while(my $d = $sth->fetch)
171 {
172 # Look for "usage" column with value "log only"
173 if($d->[2] && $d->[2] =~ /log only/)
174 {
175 # Grab "size", add it to our log size
176 $d->[1] =~ s/[^\d\.]//g;
177 $dbinfo->{log} += $d->[1];
178 }
180 # Look for "device fragments" column with "log only"
181 # followed by a number.
182 if($d->[0] =~ /log only .* (\d+)/)
183 {
184 $dbinfo->{logfree} = $1 / 1024;
185 }
186 }
187 } while($sth->{syb_more_results});
189 # Subtract the log size from the database size
190 $dbinfo->{size} -= $dbinfo->{log};
192 # The "reserved" space is free for use by the table that freed it, so
193 # it is not truly free space. To be safe, our calculation ignores it.
194 my $free = ($dbinfo->{size} - $dbinfo->{reserved}) / $dbinfo->{size};
195 $free = sprintf("%.2f", $free*100);
198 if ($free < $crit)
199 {
200 &ncrit("Free space is $free%! (critical threshold is $crit%)");
201 }
203 if ($free < $warn)
204 {
205 &nwarn("Free space is $free%! (warning threshold is $warn%)");
206 }
209 &nok("Free space within thresholds ($free% free)");
210 }
212 sub read_config
213 {
214 open (CONFIG, "<$config")
215 or &nunk("Failed to open config file '$config': $!");
216 while (<CONFIG>)
217 {
218 chomp;
219 next if m/^#/; # skip comments
220 next if m/^$/; # skip blanks
222 # Each case-insensitive argument can be followed by an optional
223 # colon, then must be followed by whitespace and the value.
224 # Options in the config file override those given on the
225 # command line, but don't rely on this!
227 if (m/USER:?\s+(\S+)/i)
228 {
229 $user = $1;
230 }
231 elsif (m/PASS:?\s+(\S+)/i)
232 {
233 $pass = $1;
234 }
235 elsif (m/DBSVR:?\s+(\S+)/i)
236 {
237 $dbsvr = $1;
238 }
239 elsif (m/DBNAME:?\s+(\S+)/i)
240 {
241 $dbname = $1;
242 }
243 else
244 {
245 &nunk("Invalid line $. in config file '$config'");
246 }
247 }
248 close (CONFIG);
249 }
251 sub help
252 {
253 print <<_HELP_;
254 Usage: $PROGNAME OPTIONS
255 A nagios plugin that connects to a Sybase database and checks free space.
257 Mandatory arguments to long options are mandatory for short options too.
258 -U, --user Username to connect to database.
259 -P, --pass Password to connect to database.
260 -S, --dbsvr Database server (as in the interfaces file).
261 -D, --dbname Database name to check.
262 --config=FILE Config file (see SECURITY below)
263 --checktype=TYPE Type of check to run (see TYPEs below)
264 -w, --warning Warning threshold, in percent (default 25)
265 -c, --critical Critical threshold, in percent (default 10)
266 -t, --timeout Timeout value, in seconds (default 30)
267 -h, --help This help message
268 -V, --version Version information ($VERSION)
270 Examples:
271 $PROGNAME -U sa -P secret -S bigbox -D orders
272 $PROGNAME --config=/secure/nagios-sybase.cfg --checktype=CONNECT
274 TYPEs
275 There are two types of checks you can run:
276 --checktype=CONNECT
277 Checks just the connection to the database.
278 --checktype=FREESPACE
279 (Default) Checks both the connection to the database and the free space.
281 SECURITY - Using a config file
282 Since a "ps ax" will reveal your database username and password, you can
283 instead specify them in a config file. Pass the config file with --config.
284 The format of the file is:
285 USER value
286 PASS value
287 You can also specify a DBSVR and DBNAME in the file. Comments (#) and blank
288 lines are ignored. Use whitespace to separate argument and value.
289 _HELP_
291 }
295 # Some wrappers..
297 # Returns code 0, OK
298 sub nok
299 {
300 my $msg = shift;
301 print "OK: $msg\n" if $msg;
303 &disconnect;
304 exit $ERRORS{OK};
305 }
307 # Returns code 1, Warning
308 sub nwarn
309 {
310 my $msg = shift;
311 print "WARNING: $msg\n";
313 &disconnect;
314 exit $ERRORS{WARNING};
315 }
317 # Returns code 2, Critical
318 sub ncrit
319 {
320 my $msg = shift;
321 print "CRITICAL: $msg\n";
323 &disconnect;
324 exit $ERRORS{CRITICAL};
325 }
327 # Returns code 3, Unknown
328 sub nunk
329 {
330 my $msg = shift;
331 print "ERROR: $msg\n";
333 &disconnect;
334 exit $ERRORS{UNKNOWN};
335 }