1 /******************************************************************************
2 *
3 * CHECK_DISK.C
4 *
5 * Program: Disk space plugin for Nagios
6 * License: GPL
7 * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org)
8 * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net)
9 *
10 * $Id$
11 *
12 * Description:
13 *
14 * This plugin will use the /bin/df command to check the free space on
15 * currently mounted filesystems. If the percent used disk space is
16 * above <c_dfp>, a STATE_CRITICAL is returned. If the percent used
17 * disk space is above <w_dfp>, a STATE_WARNING is returned. If the
18 * specified filesystem cannot be read, a STATE_CRITICAL is returned,
19 * other errors with reading the output result in a STATE_UNKNOWN
20 * error.
21 *
22 * Notes:
23 * - IRIX support added by Charlie Cook 4-16-1999
24 * - Modifications by Karl DeBisschop 1999-11-24
25 * reformat code to 80 char screen width
26 * set STATE_WARNING if stderr is written or spclose status set
27 * set default result to STAT_UNKNOWN
28 * initailize usp to -1, eliminate 'found' variable
29 * accept any filename/filesystem
30 * use sscanf, drop while loop
31 *
32 *****************************************************************************/
34 #include "common.h"
35 #include "popen.h"
36 #include "utils.h"
37 #include <stdarg.h>
39 #ifdef _AIX
40 #pragma alloca
41 #endif
43 #if HAVE_INTTYPES_H
44 # include <inttypes.h>
45 #endif
47 #define REVISION "$Revision$"
48 #define COPYRIGHT "2000-2002"
50 int process_arguments (int, char **);
51 int validate_arguments (void);
52 int check_disk (int usp, int free_disk);
53 void print_help (void);
54 void print_usage (void);
56 const char *progname = "check_disk";
58 int w_df = -1;
59 int c_df = -1;
60 float w_dfp = -1.0;
61 float c_dfp = -1.0;
62 char *path = "";
63 char *exclude_device = "";
64 int verbose = 0;
65 int erronly = FALSE;
66 int display_mntp = FALSE;
69 int
70 main (int argc, char **argv)
71 {
72 int usp = -1;
73 int total_disk = -1;
74 int used_disk = -1;
75 int free_disk = -1;
76 int result = STATE_UNKNOWN;
77 int disk_result = STATE_UNKNOWN;
78 char *command_line = "";
79 char input_buffer[MAX_INPUT_BUFFER];
80 char file_system[MAX_INPUT_BUFFER];
81 char mntp[MAX_INPUT_BUFFER];
82 char *output = "";
84 #ifdef HAVE_STRUCT_STATFS
85 #ifdef HAVE_SYS_VFS_H
86 #include <sys/vfs.h>
87 #else
88 #include <sys/param.h>
89 #include <sys/mount.h>
90 #endif
91 struct statfs buf;
92 #endif
94 if (process_arguments (argc, argv) != OK)
95 usage ("Could not parse arguments\n");
97 #ifdef HAVE_STRUCT_STATFS
99 if (statfs (path, &buf) == -1) {
100 switch (errno)
101 {
102 case ENOTDIR:
103 terminate (STATE_UNKNOWN, "A component of the path prefix is not a directory.\n");
104 case ENAMETOOLONG:
105 terminate (STATE_UNKNOWN, "path is too long.\n");
106 case ENOENT:
107 terminate (STATE_UNKNOWN, "The file referred to by path does not exist.\n");
108 case EACCES:
109 terminate (STATE_UNKNOWN, "Search permission is denied for a component of the path prefix of path.\n");
110 case ELOOP:
111 terminate (STATE_UNKNOWN, "Too many symbolic links were encountered in translating path.\n");
112 case EFAULT:
113 terminate (STATE_UNKNOWN, "Buf or path points to an invalid address.\n");
114 case EIO:
115 terminate (STATE_UNKNOWN, "An I/O error occurred while reading from or writing to the file system.\n");
116 case ENOMEM:
117 terminate (STATE_UNKNOWN, "Insufficient kernel memory was available.\n");
118 case ENOSYS:
119 terminate (STATE_UNKNOWN, "The filesystem path is on does not support statfs.\n");
120 }
121 }
122 usp = (buf.f_blocks - buf.f_bavail) / buf.f_blocks;
123 disk_result = check_disk (usp, buf.f_bavail);
124 result = disk_result;
125 asprintf (&output, "%ld of %ld kB free (%ld-byte blocks)",
126 buf.f_bavail*buf.f_bsize/1024, buf.f_blocks*buf.f_bsize/1024, buf.f_bsize);
128 #else
130 asprintf (&command_line, "%s %s", DF_COMMAND, path);
132 if (verbose>0)
133 printf ("%s ==> ", command_line);
135 child_process = spopen (command_line);
136 if (child_process == NULL) {
137 printf ("Could not open pipe: %s\n", command_line);
138 return STATE_UNKNOWN;
139 }
141 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
142 if (child_stderr == NULL) {
143 printf ("Could not open stderr for %s\n", command_line);
144 }
146 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
148 if (!index (input_buffer, '/'))
149 continue;
151 /* Fixes AIX /proc fs which lists - for size values */
152 if (strstr (input_buffer, "/proc ") == input_buffer)
153 continue;
155 if (sscanf (input_buffer, "%s %d %d %d %d%% %s", file_system,
156 &total_disk, &used_disk, &free_disk, &usp, mntp) == 6 ||
157 sscanf (input_buffer, "%s %*s %d %d %d %d%% %s", file_system,
158 &total_disk, &used_disk, &free_disk, &usp, mntp) == 6) {
160 if (strcmp(exclude_device,file_system) == 0 ||
161 strcmp(exclude_device,mntp) == 0) {
162 if (verbose>0)
163 printf ("ignoring %s.", file_system);
164 continue;
165 }
167 disk_result = check_disk (usp, free_disk);
169 if (strcmp (file_system, "none") == 0)
170 strncpy (file_system, mntp, MAX_INPUT_BUFFER-1);
172 if (disk_result==STATE_OK && erronly && !verbose)
173 continue;
175 if (disk_result!=STATE_OK || verbose>=0)
176 asprintf (&output, "%s [%d kB (%d%%) free on %s]", output,
177 free_disk, 100 - usp, display_mntp ? mntp : file_system);
179 result = max_state (result, disk_result);
180 }
182 else {
183 printf ("Unable to read output:\n%s\n%s\n", command_line, input_buffer);
184 return result;
185 }
187 }
189 /* If we get anything on stderr, at least set warning */
190 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
191 if (result != STATE_CRITICAL) {
192 result = STATE_WARNING;
193 }
194 }
196 /* close stderr */
197 if (child_stderr)
198 (void) fclose (child_stderr);
200 /* close the pipe */
201 if (spclose(child_process)!=0 && result!=STATE_CRITICAL)
202 result = STATE_WARNING;
204 if (usp < 0)
205 terminate (result, "Disk \"%s\" not mounted or nonexistant\n", path);
206 else if (result == STATE_UNKNOWN)
207 terminate (result, "Unable to read output\n%s\n%s\n", command_line, input_buffer);
209 #endif
211 terminate (result, "DISK %s %s\n", state_text (result), output);
212 }
214 /* process command-line arguments */
215 int
216 process_arguments (int argc, char **argv)
217 {
218 int c;
220 int option_index = 0;
221 static struct option long_options[] = {
222 {"warning", required_argument, 0, 'w'},
223 {"critical", required_argument, 0, 'c'},
224 {"timeout", required_argument, 0, 't'},
225 {"path", required_argument, 0, 'p'},
226 {"partition", required_argument, 0, 'p'},
227 {"verbose", no_argument, 0, 'v'},
228 {"version", no_argument, 0, 'V'},
229 {"errors-only", no_argument, 0, 'e'},
230 {"help", no_argument, 0, 'h'},
231 {"mountpoint", no_argument, 0, 'm'},
232 {"exclude_device", required_argument, 0, 'x'},
233 {"quiet", no_argument, 0, 'q'},
235 {0, 0, 0, 0}
236 };
238 if (argc < 2)
239 return ERROR;
241 for (c = 1; c < argc; c++)
242 if (strcmp ("-to", argv[c]) == 0)
243 strcpy (argv[c], "-t");
245 while (1) {
246 c = getopt_long (argc, argv, "+?Vqhvet:c:w:p:x:m", long_options, &option_index);
248 if (c == -1 || c == EOF)
249 break;
251 switch (c) {
252 case 'w': /* warning time threshold */
253 if (is_intnonneg (optarg)) {
254 w_df = atoi (optarg);
255 break;
256 }
257 else if (strpbrk (optarg, ",:") &&
258 strstr (optarg, "%") &&
259 sscanf (optarg, "%d%*[:,]%f%%", &w_df, &w_dfp) == 2) {
260 break;
261 }
262 else if (strstr (optarg, "%") && sscanf (optarg, "%f%%", &w_dfp) == 1) {
263 break;
264 }
265 else {
266 usage ("Warning threshold must be integer or percentage!\n");
267 }
268 case 'c': /* critical time threshold */
269 if (is_intnonneg (optarg)) {
270 c_df = atoi (optarg);
271 break;
272 }
273 else if (strpbrk (optarg, ",:") &&
274 strstr (optarg, "%") &&
275 sscanf (optarg, "%d%*[,:]%f%%", &c_df, &c_dfp) == 2) {
276 break;
277 }
278 else if (strstr (optarg, "%") && sscanf (optarg, "%f%%", &c_dfp) == 1) {
279 break;
280 }
281 else {
282 usage ("Critical threshold must be integer or percentage!\n");
283 }
284 case 't': /* timeout period */
285 if (is_integer (optarg)) {
286 timeout_interval = atoi (optarg);
287 break;
288 }
289 else {
290 usage ("Timeout Interval must be an integer!\n");
291 }
292 case 'p': /* path or partition */
293 path = optarg;
294 break;
295 case 'v': /* verbose */
296 verbose++;
297 break;
298 case 'q': /* verbose */
299 verbose--;
300 break;
301 case 'e':
302 erronly = TRUE;
303 break;
304 case 'm': /* display mountpoint */
305 display_mntp = TRUE;
306 break;
307 case 'x': /* exclude path or partition */
308 exclude_device = optarg;
309 break;
310 case 'V': /* version */
311 print_revision (progname, REVISION);
312 exit (STATE_OK);
313 case 'h': /* help */
314 print_help ();
315 exit (STATE_OK);
316 case '?': /* help */
317 usage ("check_disk: unrecognized option\n");
318 break;
319 }
320 }
322 c = optind;
323 if (w_dfp == -1 && argc > c && is_intnonneg (argv[c]))
324 w_dfp = (100.0 - atof (argv[c++]));
326 if (c_dfp == -1 && argc > c && is_intnonneg (argv[c]))
327 c_dfp = (100.0 - atof (argv[c++]));
329 if (argc > c && strlen (path) == 0)
330 path = argv[c++];
332 return validate_arguments ();
333 }
335 int
336 validate_arguments ()
337 {
338 if (w_df < 0 && c_df < 0 && w_dfp < 0 && c_dfp < 0) {
339 printf ("INPUT ERROR: Unable to parse command line\n");
340 return ERROR;
341 }
342 else if ((w_dfp >= 0 || c_dfp >= 0)
343 && (w_dfp < 0 || c_dfp < 0 || w_dfp > 100 || c_dfp > 100
344 || c_dfp > w_dfp)) {
345 printf
346 ("INPUT ERROR: C_DFP (%f) should be less than W_DFP (%f) and both should be between zero and 100 percent, inclusive\n",
347 c_dfp, w_dfp);
348 return ERROR;
349 }
350 else if ((w_df > 0 || c_df > 0) && (w_df < 0 || c_df < 0 || c_df > w_df)) {
351 printf
352 ("INPUT ERROR: C_DF (%d) should be less than W_DF (%d) and both should be greater than zero\n",
353 c_df, w_df);
354 return ERROR;
355 }
356 else {
357 return OK;
358 }
359 }
361 int
362 check_disk (usp, free_disk)
363 {
364 int result = STATE_UNKNOWN;
365 /* check the percent used space against thresholds */
366 if (usp >= 0 && usp >= (100.0 - c_dfp))
367 result = STATE_CRITICAL;
368 else if (c_df >= 0 && free_disk <= c_df)
369 result = STATE_CRITICAL;
370 else if (usp >= 0 && usp >= (100.0 - w_dfp))
371 result = STATE_WARNING;
372 else if (w_df >= 0 && free_disk <= w_df)
373 result = STATE_WARNING;
374 else if (usp >= 0.0)
375 result = STATE_OK;
376 return result;
377 }
379 void
380 print_help (void)
381 {
382 print_revision (progname, REVISION);
383 printf
384 ("Copyright (c) 2000 Ethan Galstad/Karl DeBisschop\n\n"
385 "This plugin will check the percent of used disk space on a mounted\n"
386 "file system and generate an alert if percentage is above one of the\n"
387 "threshold values.\n\n");
388 print_usage ();
389 printf
390 ("\nOptions:\n"
391 " -w, --warning=INTEGER\n"
392 " Exit with WARNING status if less than INTEGER kilobytes of disk are free\n"
393 " -w, --warning=PERCENT%%\n"
394 " Exit with WARNING status if less than PERCENT of disk space is free\n"
395 " -c, --critical=INTEGER\n"
396 " Exit with CRITICAL status if less than INTEGER kilobytes of disk are free\n"
397 " -c, --critical=PERCENT%%\n"
398 " Exit with CRITCAL status if less than PERCENT of disk space is free\n"
399 " -p, --path=PATH, --partition=PARTTION\n"
400 " Path or partition (checks all mounted partitions if unspecified)\n"
401 " -m, --mountpoint\n"
402 " Display the mountpoint instead of the partition\n"
403 " -x, --exclude_device=PATH\n"
404 " Ignore device (only works if -p unspecified)\n"
405 " -e, --errors-only\n"
406 " Display only devices/mountpoints with errors\n"
407 " -v, --verbose\n"
408 " Show details for command-line debugging (do not use with nagios server)\n"
409 " -h, --help\n"
410 " Print detailed help screen\n"
411 " -V, --version\n" " Print version information\n\n");
412 support ();
413 }
415 void
416 print_usage (void)
417 {
418 printf
419 ("Usage: %s -w limit -c limit [-p path | -x device] [-t timeout] [-m] [-e] [--verbose]\n"
420 " %s (-h|--help)\n"
421 " %s (-V|--version)\n", progname, progname, progname);
422 }