1 /******************************************************************************
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 *****************************************************************************/
19 const char *progname = "check_disk";
20 const char *revision = "$Revision$";
21 const char *copyright = "1999-2003";
22 const char *authors = "Nagios Plugin Development Team";
23 const char *email = "nagiosplug-devel@lists.sourceforge.net";
25 const char *summary = "\
26 This plugin checks the amount of used disk space on a mounted file system\n\
27 and generates an alert if free space is less than one of the threshold values.";
29 const char *option_summary = "\
30 -w limit -c limit [-p path | -x device] [-t timeout] [-m] [-e]\n\
31 [-v] [-q]";
33 const char *options = "\
34 -w, --warning=INTEGER\n\
35 Exit with WARNING status if less than INTEGER kilobytes of disk are free\n\
36 -w, --warning=PERCENT%%\n\
37 Exit with WARNING status if less than PERCENT of disk space is free\n\
38 -c, --critical=INTEGER\n\
39 Exit with CRITICAL status if less than INTEGER kilobytes of disk are free\n\
40 -c, --critical=PERCENT%%\n\
41 Exit with CRITCAL status if less than PERCENT of disk space is free\n\
42 -p, --path=PATH, --partition=PARTTION\n\
43 Path or partition (checks all mounted partitions if unspecified)\n\
44 -m, --mountpoint\n\
45 Display the mountpoint instead of the partition\n\
46 -x, --exclude_device=PATH\n\
47 Ignore device (only works if -p unspecified)\n\
48 -e, --errors-only\n\
49 Display only devices/mountpoints with errors\n\
50 -v, --verbose\n\
51 Show details for command-line debugging (do not use with nagios server)\n\
52 -h, --help\n\
53 Print detailed help screen\n\
54 -V, --version\n\
55 Print version information\n";
57 #include "common.h"
58 #if HAVE_INTTYPES_H
59 # include <inttypes.h>
60 #endif
61 #include <assert.h>
62 #include "popen.h"
63 #include "utils.h"
64 #include <stdarg.h>
65 #include "../lib/fsusage.h"
66 #include "../lib/mountlist.h"
68 /* If nonzero, show inode information. */
69 static int inode_format;
71 /* If nonzero, show even filesystems with zero size or
72 uninteresting types. */
73 static int show_all_fs;
75 /* If nonzero, show only local filesystems. */
76 static int show_local_fs;
78 /* If nonzero, output data for each filesystem corresponding to a
79 command line argument -- even if it's a dummy (automounter) entry. */
80 static int show_listed_fs;
82 /* If positive, the units to use when printing sizes;
83 if negative, the human-readable base. */
84 static int output_block_size;
86 /* If nonzero, invoke the `sync' system call before getting any usage data.
87 Using this option can make df very slow, especially with many or very
88 busy disks. Note that this may make a difference on some systems --
89 SunOs4.1.3, for one. It is *not* necessary on Linux. */
90 static int require_sync = 0;
92 /* A filesystem type to display. */
94 struct name_list
95 {
96 char *name;
97 struct name_list *name_next;
98 };
100 /* Linked list of filesystem types to display.
101 If `fs_select_list' is NULL, list all types.
102 This table is generated dynamically from command-line options,
103 rather than hardcoding into the program what it thinks are the
104 valid filesystem types; let the user specify any filesystem type
105 they want to, and if there are any filesystems of that type, they
106 will be shown.
108 Some filesystem types:
109 4.2 4.3 ufs nfs swap ignore io vm efs dbg */
111 static struct name_list *fs_select_list;
113 /* Linked list of filesystem types to omit.
114 If the list is empty, don't exclude any types. */
116 static struct name_list *fs_exclude_list;
118 static struct name_list *dp_exclude_list;
120 static struct name_list *path_select_list;
122 static struct name_list *dev_select_list;
124 /* Linked list of mounted filesystems. */
125 static struct mount_entry *mount_list;
127 /* For long options that have no equivalent short option, use a
128 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
129 enum
130 {
131 SYNC_OPTION = CHAR_MAX + 1,
132 NO_SYNC_OPTION,
133 BLOCK_SIZE_OPTION
134 };
136 #ifdef _AIX
137 #pragma alloca
138 #endif
140 int process_arguments (int, char **);
141 int validate_arguments (void);
142 int check_disk (int usp, int free_disk);
143 int walk_name_list (struct name_list *list, const char *name);
144 void print_help (void);
145 void print_usage (void);
147 int w_df = -1;
148 int c_df = -1;
149 float w_dfp = -1.0;
150 float c_dfp = -1.0;
151 char *path = "";
152 char *exclude_device = "";
153 int verbose = 0;
154 int erronly = FALSE;
155 int display_mntp = FALSE;
157 /* Linked list of mounted filesystems. */
158 static struct mount_entry *mount_list;
160 int
161 main (int argc, char **argv)
162 {
163 int usp = -1;
164 int total_disk = -1;
165 int used_disk = -1;
166 int free_disk = -1;
167 int result = STATE_UNKNOWN;
168 int disk_result = STATE_UNKNOWN;
169 char *command_line = "";
170 char input_buffer[MAX_INPUT_BUFFER];
171 char file_system[MAX_INPUT_BUFFER];
172 char mntp[MAX_INPUT_BUFFER];
173 char *output = "";
174 char *details = "";
176 struct mount_entry *me;
177 struct fs_usage fsp;
178 char *disk;
180 mount_list = read_filesystem_list (0);
182 if (process_arguments (argc, argv) != OK)
183 usage ("Could not parse arguments\n");
185 for (me = mount_list; me; me = me->me_next) {
187 if ((dev_select_list &&
188 walk_name_list (dev_select_list, me->me_devname)) ||
189 (path_select_list &&
190 walk_name_list (path_select_list, me->me_mountdir)))
191 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
192 else if (dev_select_list || path_select_list)
193 continue;
194 else if (fs_exclude_list && walk_name_list (fs_exclude_list, me->me_type))
195 continue;
196 else if (dp_exclude_list &&
197 walk_name_list (dp_exclude_list, me->me_devname) ||
198 walk_name_list (dp_exclude_list, me->me_mountdir))
199 continue;
200 else
201 get_fs_usage (me->me_mountdir, me->me_devname, &fsp);
203 if (fsp.fsu_blocks && strcmp ("none", me->me_mountdir)) {
204 usp = (fsp.fsu_blocks - fsp.fsu_bavail) * 100 / fsp.fsu_blocks;
205 disk_result = check_disk (usp, fsp.fsu_bavail);
206 result = max_state (disk_result, result);
207 if (disk_result==STATE_OK && erronly && !verbose)
208 continue;
210 if (disk_result!=STATE_OK || verbose>=0)
211 asprintf (&output, "%s [%llu MB (%2.0f%%) free on %s]",
212 output,
213 fsp.fsu_bavail*fsp.fsu_blocksize/1024/1024,
214 (double)fsp.fsu_bavail*100/fsp.fsu_blocks,
215 (!strcmp(file_system, "none") || display_mntp) ? me->me_devname : me->me_mountdir);
216 asprintf (&details, "%s\n%llu of %llu MB (%2.0f%%) free on %s (type %s mounted on %s)",
217 details,
218 fsp.fsu_bavail*fsp.fsu_blocksize/1024/1024,
219 fsp.fsu_blocks*fsp.fsu_blocksize/1024/1024,
220 (double)fsp.fsu_bavail*100/fsp.fsu_blocks,
221 me->me_devname,
222 me->me_type,
223 me->me_mountdir);
224 }
226 }
228 if (verbose > 2)
229 asprintf (&output, "%s%s", output, details);
231 terminate (result, "DISK %s%s\n", state_text (result), output, details);
232 }
236 \f
237 /* process command-line arguments */
238 int
239 process_arguments (int argc, char **argv)
240 {
241 int c;
242 struct name_list *se;
243 struct name_list **pathtail = &path_select_list;
244 struct name_list **devtail = &dev_select_list;
245 struct name_list **fstail = &fs_exclude_list;
246 struct name_list **dptail = &dp_exclude_list;
248 int option_index = 0;
249 static struct option long_options[] = {
250 {"warning", required_argument, 0, 'w'},
251 {"critical", required_argument, 0, 'c'},
252 {"timeout", required_argument, 0, 't'},
253 {"path", required_argument, 0, 'p'},
254 {"partition", required_argument, 0, 'p'},
255 {"verbose", no_argument, 0, 'v'},
256 {"version", no_argument, 0, 'V'},
257 {"errors-only", no_argument, 0, 'e'},
258 {"help", no_argument, 0, 'h'},
259 {"mountpoint", no_argument, 0, 'm'},
260 {"device", no_argument, 0, 'd'},
261 {"exclude_device", required_argument, 0, 'x'},
262 {"quiet", no_argument, 0, 'q'},
264 {0, 0, 0, 0}
265 };
267 if (argc < 2)
268 return ERROR;
270 for (c = 1; c < argc; c++)
271 if (strcmp ("-to", argv[c]) == 0)
272 strcpy (argv[c], "-t");
274 while (1) {
275 c = getopt_long (argc, argv, "+?Vqhvet:c:w:p:d:x:X:m", long_options, &option_index);
277 if (c == -1 || c == EOF)
278 break;
280 switch (c) {
281 case 'w': /* warning time threshold */
282 if (is_intnonneg (optarg)) {
283 w_df = atoi (optarg);
284 break;
285 }
286 else if (strpbrk (optarg, ",:") &&
287 strstr (optarg, "%") &&
288 sscanf (optarg, "%d%*[:,]%f%%", &w_df, &w_dfp) == 2) {
289 break;
290 }
291 else if (strstr (optarg, "%") && sscanf (optarg, "%f%%", &w_dfp) == 1) {
292 break;
293 }
294 else {
295 usage ("Warning threshold must be integer or percentage!\n");
296 }
297 case 'c': /* critical time threshold */
298 if (is_intnonneg (optarg)) {
299 c_df = atoi (optarg);
300 break;
301 }
302 else if (strpbrk (optarg, ",:") &&
303 strstr (optarg, "%") &&
304 sscanf (optarg, "%d%*[,:]%f%%", &c_df, &c_dfp) == 2) {
305 break;
306 }
307 else if (strstr (optarg, "%") && sscanf (optarg, "%f%%", &c_dfp) == 1) {
308 break;
309 }
310 else {
311 usage ("Critical threshold must be integer or percentage!\n");
312 }
313 case 't': /* timeout period */
314 if (is_integer (optarg)) {
315 timeout_interval = atoi (optarg);
316 break;
317 }
318 else {
319 usage ("Timeout Interval must be an integer!\n");
320 }
321 case 'p': /* selec path */
322 se = (struct name_list *) malloc (sizeof (struct name_list));
323 se->name = strdup (optarg);
324 se->name_next = NULL;
325 *pathtail = se;
326 pathtail = &se->name_next;
327 break;
328 case 'd': /* select partition/device */
329 se = (struct name_list *) malloc (sizeof (struct name_list));
330 se->name = strdup (optarg);
331 se->name_next = NULL;
332 *devtail = se;
333 devtail = &se->name_next;
334 break;
335 case 'x': /* exclude path or partition */
336 se = (struct name_list *) malloc (sizeof (struct name_list));
337 se->name = strdup (optarg);
338 se->name_next = NULL;
339 *dptail = se;
340 dptail = &se->name_next;
341 break;
342 break;
343 case 'X': /* exclude file system type */
344 se = (struct name_list *) malloc (sizeof (struct name_list));
345 se->name = strdup (optarg);
346 se->name_next = NULL;
347 *fstail = se;
348 fstail = &se->name_next;
349 break;
350 case 'v': /* verbose */
351 verbose++;
352 break;
353 case 'q': /* verbose */
354 verbose--;
355 break;
356 case 'e':
357 erronly = TRUE;
358 break;
359 case 'm': /* display mountpoint */
360 display_mntp = TRUE;
361 break;
362 case 'V': /* version */
363 print_revision (progname, revision);
364 exit (STATE_OK);
365 case 'h': /* help */
366 print_help ();
367 exit (STATE_OK);
368 case '?': /* help */
369 usage ("check_disk: unrecognized option\n");
370 break;
371 }
372 }
374 c = optind;
375 if (w_dfp == -1 && argc > c && is_intnonneg (argv[c]))
376 w_dfp = (100.0 - atof (argv[c++]));
378 if (c_dfp == -1 && argc > c && is_intnonneg (argv[c]))
379 c_dfp = (100.0 - atof (argv[c++]));
381 if (argc > c && strlen (path) == 0)
382 path = argv[c++];
384 return validate_arguments ();
385 }
389 int
390 validate_arguments ()
391 {
392 if (w_df < 0 && c_df < 0 && w_dfp < 0 && c_dfp < 0) {
393 printf ("INPUT ERROR: Unable to parse command line\n");
394 return ERROR;
395 }
396 else if ((w_dfp >= 0 || c_dfp >= 0)
397 && (w_dfp < 0 || c_dfp < 0 || w_dfp > 100 || c_dfp > 100
398 || c_dfp > w_dfp)) {
399 printf
400 ("INPUT ERROR: C_DFP (%f) should be less than W_DFP (%f) and both should be between zero and 100 percent, inclusive\n",
401 c_dfp, w_dfp);
402 return ERROR;
403 }
404 else if ((w_df > 0 || c_df > 0) && (w_df < 0 || c_df < 0 || c_df > w_df)) {
405 printf
406 ("INPUT ERROR: C_DF (%d) should be less than W_DF (%d) and both should be greater than zero\n",
407 c_df, w_df);
408 return ERROR;
409 }
410 else {
411 return OK;
412 }
413 }
417 \f
418 int
419 check_disk (int usp, int free_disk)
420 {
421 int result = STATE_UNKNOWN;
422 /* check the percent used space against thresholds */
423 if (usp >= 0 && usp >= (100.0 - c_dfp))
424 result = STATE_CRITICAL;
425 else if (c_df >= 0 && free_disk <= c_df)
426 result = STATE_CRITICAL;
427 else if (usp >= 0 && usp >= (100.0 - w_dfp))
428 result = STATE_WARNING;
429 else if (w_df >= 0 && free_disk <= w_df)
430 result = STATE_WARNING;
431 else if (usp >= 0.0)
432 result = STATE_OK;
433 return result;
434 }
438 int
439 walk_name_list (struct name_list *list, const char *name)
440 {
441 while (list) {
442 if (! strcmp(list->name, name))
443 return TRUE;
444 list = list->name_next;
445 }
446 return FALSE;
447 }
451 \f
452 void
453 print_help (void)
454 {
455 print_revision (progname, revision);
456 printf ("Copyright (c) %s %s\n\t<%s>\n\n%s\n",
457 copyright, authors, email, summary);
458 print_usage ();
459 printf ("\nOptions:\n");
460 printf (options);
461 support ();
462 }
464 void
465 print_usage (void)
466 {
467 printf
468 ("Usage: %s %s\n"
469 " %s (-h|--help)\n"
470 " %s (-V|--version)\n", progname, option_summary, progname, progname);
471 }