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 #ifdef ENOTDIR
103 case ENOTDIR:
104 terminate (STATE_UNKNOWN, "A component of the path prefix is not a directory.\n");
105 #endif
106 #ifdef ENAMETOOLONG
107 case ENAMETOOLONG:
108 terminate (STATE_UNKNOWN, "path is too long.\n");
109 #endif
110 #ifdef ENOENT
111 case ENOENT:
112 terminate (STATE_UNKNOWN, "The file referred to by path does not exist.\n");
113 #endif
114 #ifdef EACCES
115 case EACCES:
116 terminate (STATE_UNKNOWN, "Search permission is denied for a component of the path prefix of path.\n");
117 #endif
118 #ifdef ELOOP
119 case ELOOP:
120 terminate (STATE_UNKNOWN, "Too many symbolic links were encountered in translating path.\n");
121 #endif
122 #ifdef EFAULT
123 case EFAULT:
124 terminate (STATE_UNKNOWN, "Buf or path points to an invalid address.\n");
125 #endif
126 #ifdef EIO
127 case EIO:
128 terminate (STATE_UNKNOWN, "An I/O error occurred while reading from or writing to the file system.\n");
129 #endif
130 #ifdef ENOMEM
131 case ENOMEM:
132 terminate (STATE_UNKNOWN, "Insufficient kernel memory was available.\n");
133 #endif
134 #ifdef ENOSYS
135 case ENOSYS:
136 terminate (STATE_UNKNOWN, "The filesystem path is on does not support statfs.\n");
137 #endif
138 }
139 }
141 usp = (buf.f_blocks - buf.f_bavail) / buf.f_blocks;
142 disk_result = check_disk (usp, buf.f_bavail);
143 result = disk_result;
144 asprintf (&output, "%ld of %ld kB free (%ld-byte blocks)",
145 buf.f_bavail*buf.f_bsize/1024, buf.f_blocks*buf.f_bsize/1024, buf.f_bsize);
147 #else
149 asprintf (&command_line, "%s %s", DF_COMMAND, path);
151 if (verbose>0)
152 printf ("%s ==> ", command_line);
154 child_process = spopen (command_line);
155 if (child_process == NULL) {
156 printf ("Could not open pipe: %s\n", command_line);
157 return STATE_UNKNOWN;
158 }
160 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
161 if (child_stderr == NULL) {
162 printf ("Could not open stderr for %s\n", command_line);
163 }
165 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
167 if (!index (input_buffer, '/'))
168 continue;
170 /* Fixes AIX /proc fs which lists - for size values */
171 if (strstr (input_buffer, "/proc ") == input_buffer)
172 continue;
174 if (sscanf (input_buffer, "%s %d %d %d %d%% %s", file_system,
175 &total_disk, &used_disk, &free_disk, &usp, mntp) == 6 ||
176 sscanf (input_buffer, "%s %*s %d %d %d %d%% %s", file_system,
177 &total_disk, &used_disk, &free_disk, &usp, mntp) == 6) {
179 if (strcmp(exclude_device,file_system) == 0 ||
180 strcmp(exclude_device,mntp) == 0) {
181 if (verbose>0)
182 printf ("ignoring %s.", file_system);
183 continue;
184 }
186 disk_result = check_disk (usp, free_disk);
188 if (strcmp (file_system, "none") == 0)
189 strncpy (file_system, mntp, MAX_INPUT_BUFFER-1);
191 if (disk_result==STATE_OK && erronly && !verbose)
192 continue;
194 if (disk_result!=STATE_OK || verbose>=0)
195 asprintf (&output, "%s [%d kB (%d%%) free on %s]", output,
196 free_disk, 100 - usp, display_mntp ? mntp : file_system);
198 result = max_state (result, disk_result);
199 }
201 else {
202 printf ("Unable to read output:\n%s\n%s\n", command_line, input_buffer);
203 return result;
204 }
206 }
208 /* If we get anything on stderr, at least set warning */
209 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
210 if (result != STATE_CRITICAL) {
211 result = STATE_WARNING;
212 }
213 }
215 /* close stderr */
216 if (child_stderr)
217 (void) fclose (child_stderr);
219 /* close the pipe */
220 if (spclose(child_process)!=0 && result!=STATE_CRITICAL)
221 result = STATE_WARNING;
223 if (usp < 0)
224 terminate (result, "Disk \"%s\" not mounted or nonexistant\n", path);
225 else if (result == STATE_UNKNOWN)
226 terminate (result, "Unable to read output\n%s\n%s\n", command_line, input_buffer);
228 #endif
230 terminate (result, "DISK %s %s\n", state_text (result), output);
231 }
233 /* process command-line arguments */
234 int
235 process_arguments (int argc, char **argv)
236 {
237 int c;
239 int option_index = 0;
240 static struct option long_options[] = {
241 {"warning", required_argument, 0, 'w'},
242 {"critical", required_argument, 0, 'c'},
243 {"timeout", required_argument, 0, 't'},
244 {"path", required_argument, 0, 'p'},
245 {"partition", required_argument, 0, 'p'},
246 {"verbose", no_argument, 0, 'v'},
247 {"version", no_argument, 0, 'V'},
248 {"errors-only", no_argument, 0, 'e'},
249 {"help", no_argument, 0, 'h'},
250 {"mountpoint", no_argument, 0, 'm'},
251 {"exclude_device", required_argument, 0, 'x'},
252 {"quiet", no_argument, 0, 'q'},
254 {0, 0, 0, 0}
255 };
257 if (argc < 2)
258 return ERROR;
260 for (c = 1; c < argc; c++)
261 if (strcmp ("-to", argv[c]) == 0)
262 strcpy (argv[c], "-t");
264 while (1) {
265 c = getopt_long (argc, argv, "+?Vqhvet:c:w:p:x:m", long_options, &option_index);
267 if (c == -1 || c == EOF)
268 break;
270 switch (c) {
271 case 'w': /* warning time threshold */
272 if (is_intnonneg (optarg)) {
273 w_df = atoi (optarg);
274 break;
275 }
276 else if (strpbrk (optarg, ",:") &&
277 strstr (optarg, "%") &&
278 sscanf (optarg, "%d%*[:,]%f%%", &w_df, &w_dfp) == 2) {
279 break;
280 }
281 else if (strstr (optarg, "%") && sscanf (optarg, "%f%%", &w_dfp) == 1) {
282 break;
283 }
284 else {
285 usage ("Warning threshold must be integer or percentage!\n");
286 }
287 case 'c': /* critical time threshold */
288 if (is_intnonneg (optarg)) {
289 c_df = atoi (optarg);
290 break;
291 }
292 else if (strpbrk (optarg, ",:") &&
293 strstr (optarg, "%") &&
294 sscanf (optarg, "%d%*[,:]%f%%", &c_df, &c_dfp) == 2) {
295 break;
296 }
297 else if (strstr (optarg, "%") && sscanf (optarg, "%f%%", &c_dfp) == 1) {
298 break;
299 }
300 else {
301 usage ("Critical threshold must be integer or percentage!\n");
302 }
303 case 't': /* timeout period */
304 if (is_integer (optarg)) {
305 timeout_interval = atoi (optarg);
306 break;
307 }
308 else {
309 usage ("Timeout Interval must be an integer!\n");
310 }
311 case 'p': /* path or partition */
312 path = optarg;
313 break;
314 case 'v': /* verbose */
315 verbose++;
316 break;
317 case 'q': /* verbose */
318 verbose--;
319 break;
320 case 'e':
321 erronly = TRUE;
322 break;
323 case 'm': /* display mountpoint */
324 display_mntp = TRUE;
325 break;
326 case 'x': /* exclude path or partition */
327 exclude_device = optarg;
328 break;
329 case 'V': /* version */
330 print_revision (progname, REVISION);
331 exit (STATE_OK);
332 case 'h': /* help */
333 print_help ();
334 exit (STATE_OK);
335 case '?': /* help */
336 usage ("check_disk: unrecognized option\n");
337 break;
338 }
339 }
341 c = optind;
342 if (w_dfp == -1 && argc > c && is_intnonneg (argv[c]))
343 w_dfp = (100.0 - atof (argv[c++]));
345 if (c_dfp == -1 && argc > c && is_intnonneg (argv[c]))
346 c_dfp = (100.0 - atof (argv[c++]));
348 if (argc > c && strlen (path) == 0)
349 path = argv[c++];
351 return validate_arguments ();
352 }
354 int
355 validate_arguments ()
356 {
357 if (w_df < 0 && c_df < 0 && w_dfp < 0 && c_dfp < 0) {
358 printf ("INPUT ERROR: Unable to parse command line\n");
359 return ERROR;
360 }
361 else if ((w_dfp >= 0 || c_dfp >= 0)
362 && (w_dfp < 0 || c_dfp < 0 || w_dfp > 100 || c_dfp > 100
363 || c_dfp > w_dfp)) {
364 printf
365 ("INPUT ERROR: C_DFP (%f) should be less than W_DFP (%f) and both should be between zero and 100 percent, inclusive\n",
366 c_dfp, w_dfp);
367 return ERROR;
368 }
369 else if ((w_df > 0 || c_df > 0) && (w_df < 0 || c_df < 0 || c_df > w_df)) {
370 printf
371 ("INPUT ERROR: C_DF (%d) should be less than W_DF (%d) and both should be greater than zero\n",
372 c_df, w_df);
373 return ERROR;
374 }
375 else {
376 return OK;
377 }
378 }
380 int
381 check_disk (usp, free_disk)
382 {
383 int result = STATE_UNKNOWN;
384 /* check the percent used space against thresholds */
385 if (usp >= 0 && usp >= (100.0 - c_dfp))
386 result = STATE_CRITICAL;
387 else if (c_df >= 0 && free_disk <= c_df)
388 result = STATE_CRITICAL;
389 else if (usp >= 0 && usp >= (100.0 - w_dfp))
390 result = STATE_WARNING;
391 else if (w_df >= 0 && free_disk <= w_df)
392 result = STATE_WARNING;
393 else if (usp >= 0.0)
394 result = STATE_OK;
395 return result;
396 }
398 void
399 print_help (void)
400 {
401 print_revision (progname, REVISION);
402 printf
403 ("Copyright (c) 2000 Ethan Galstad/Karl DeBisschop\n\n"
404 "This plugin will check the percent of used disk space on a mounted\n"
405 "file system and generate an alert if percentage is above one of the\n"
406 "threshold values.\n\n");
407 print_usage ();
408 printf
409 ("\nOptions:\n"
410 " -w, --warning=INTEGER\n"
411 " Exit with WARNING status if less than INTEGER kilobytes of disk are free\n"
412 " -w, --warning=PERCENT%%\n"
413 " Exit with WARNING status if less than PERCENT of disk space is free\n"
414 " -c, --critical=INTEGER\n"
415 " Exit with CRITICAL status if less than INTEGER kilobytes of disk are free\n"
416 " -c, --critical=PERCENT%%\n"
417 " Exit with CRITCAL status if less than PERCENT of disk space is free\n"
418 " -p, --path=PATH, --partition=PARTTION\n"
419 " Path or partition (checks all mounted partitions if unspecified)\n"
420 " -m, --mountpoint\n"
421 " Display the mountpoint instead of the partition\n"
422 " -x, --exclude_device=PATH\n"
423 " Ignore device (only works if -p unspecified)\n"
424 " -e, --errors-only\n"
425 " Display only devices/mountpoints with errors\n"
426 " -v, --verbose\n"
427 " Show details for command-line debugging (do not use with nagios server)\n"
428 " -h, --help\n"
429 " Print detailed help screen\n"
430 " -V, --version\n" " Print version information\n\n");
431 support ();
432 }
434 void
435 print_usage (void)
436 {
437 printf
438 ("Usage: %s -w limit -c limit [-p path | -x device] [-t timeout] [-m] [-e] [--verbose]\n"
439 " %s (-h|--help)\n"
440 " %s (-V|--version)\n", progname, progname, progname);
441 }