1 /******************************************************************************
2 *
3 * Program: Swap space plugin for Nagios
4 * License: GPL
5 *
6 * License Information:
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net)
23 *
24 * $Id$
25 *
26 *****************************************************************************/
28 const char *progname = "check_swap";
29 const char *revision = "$Revision$";
30 const char *copyright = "2000-2004";
31 const char *email = "nagiosplug-devel@lists.sourceforge.net";
33 #include "common.h"
34 #include "popen.h"
35 #include "utils.h"
37 int check_swap (int usp, float free_swap_mb);
38 int process_arguments (int argc, char **argv);
39 int validate_arguments (void);
40 void print_usage (void);
41 void print_help (void);
43 int warn_percent = 0;
44 int crit_percent = 0;
45 float warn_size_bytes = 0;
46 float crit_size_bytes= 0;
47 int verbose;
48 int allswaps;
50 int
51 main (int argc, char **argv)
52 {
53 int percent_used, percent;
54 float total_swap_mb = 0, used_swap_mb = 0, free_swap_mb = 0;
55 float dsktotal_mb = 0, dskused_mb = 0, dskfree_mb = 0, tmp_mb = 0;
56 int result = STATE_UNKNOWN;
57 char input_buffer[MAX_INPUT_BUFFER];
58 #ifdef HAVE_PROC_MEMINFO
59 FILE *fp;
60 #else
61 int conv_factor = SWAP_CONVERSION;
62 # ifdef HAVE_SWAP
63 char *temp_buffer;
64 char *swap_command;
65 char *swap_format;
66 # else
67 # ifdef HAVE_DECL_SWAPCTL
68 int i=0, nswaps=0, swapctl_res=0;
69 # ifdef CHECK_SWAP_SWAPCTL_SVR4
70 swaptbl_t *tbl=NULL;
71 swapent_t *ent=NULL;
72 # else
73 # ifdef CHECK_SWAP_SWAPCTL_BSD
74 struct swapent *ent;
75 # endif /* CHECK_SWAP_SWAPCTL_BSD */
76 # endif /* CHECK_SWAP_SWAPCTL_SVR4 */
77 # endif /* HAVE_DECL_SWAPCTL */
78 # endif
79 #endif
80 char str[32];
81 char *status;
83 setlocale (LC_ALL, "");
84 bindtextdomain (PACKAGE, LOCALEDIR);
85 textdomain (PACKAGE);
87 status = strdup ("");
89 if (process_arguments (argc, argv) == ERROR)
90 usage4 (_("Could not parse arguments"));
92 #ifdef HAVE_PROC_MEMINFO
93 if (verbose >= 3) {
94 printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
95 }
96 fp = fopen (PROC_MEMINFO, "r");
97 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) {
98 if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %f %f %f", &dsktotal_mb, &dskused_mb, &dskfree_mb) == 3) {
99 dsktotal_mb = dsktotal_mb / 1048576; /* Apply conversion */
100 dskused_mb = dskused_mb / 1048576;
101 dskfree_mb = dskfree_mb / 1048576;
102 total_swap_mb += dsktotal_mb;
103 used_swap_mb += dskused_mb;
104 free_swap_mb += dskfree_mb;
105 if (allswaps) {
106 if (dsktotal_mb == 0)
107 percent=100.0;
108 else
109 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
110 result = max_state (result, check_swap (percent, dskfree_mb));
111 if (verbose)
112 asprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
113 }
114 }
115 else if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%[TotalFre]%*[:] %f %*[k]%*[B]", str, &tmp_mb)) {
116 if (verbose >= 3) {
117 printf("Got %s with %f\n", str, tmp_mb);
118 }
119 /* I think this part is always in Kb, so convert to mb */
120 if (strcmp ("Total", str) == 0) {
121 dsktotal_mb = tmp_mb / 1024;
122 }
123 else if (strcmp ("Free", str) == 0) {
124 dskfree_mb = tmp_mb / 1024;
125 }
126 }
127 }
128 fclose(fp);
129 dskused_mb = dsktotal_mb - dskfree_mb;
130 total_swap_mb = dsktotal_mb;
131 used_swap_mb = dskused_mb;
132 free_swap_mb = dskfree_mb;
133 #else
134 # ifdef HAVE_SWAP
135 asprintf(&swap_command, "%s", SWAP_COMMAND);
136 asprintf(&swap_format, "%s", SWAP_FORMAT);
138 /* These override the command used if a summary (and thus ! allswaps) is required */
139 /* The summary flag returns more accurate information about swap usage on these OSes */
140 # ifdef _AIX
141 if (!allswaps) {
142 asprintf(&swap_command, "%s", "/usr/sbin/lsps -s");
143 asprintf(&swap_format, "%s", "%f%*s %f");
144 conv_factor = 1;
145 }
146 # endif
148 if (verbose >= 2)
149 printf (_("Command: %s\n"), swap_command);
150 if (verbose >= 3)
151 printf (_("Format: %s\n"), swap_format);
153 child_process = spopen (swap_command);
154 if (child_process == NULL) {
155 printf (_("Could not open pipe: %s\n"), swap_command);
156 return STATE_UNKNOWN;
157 }
159 child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
160 if (child_stderr == NULL)
161 printf (_("Could not open stderr for %s\n"), swap_command);
163 sprintf (str, "%s", "");
164 /* read 1st line */
165 fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
166 if (strcmp (swap_format, "") == 0) {
167 temp_buffer = strtok (input_buffer, " \n");
168 while (temp_buffer) {
169 if (strstr (temp_buffer, "blocks"))
170 sprintf (str, "%s %s", str, "%f");
171 else if (strstr (temp_buffer, "dskfree"))
172 sprintf (str, "%s %s", str, "%f");
173 else
174 sprintf (str, "%s %s", str, "%*s");
175 temp_buffer = strtok (NULL, " \n");
176 }
177 }
179 /* If different swap command is used for summary switch, need to read format differently */
180 # ifdef _AIX
181 if (!allswaps) {
182 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */
183 sscanf (input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
184 free_swap_mb = total_swap_mb * (100 - used_swap_mb) /100;
185 used_swap_mb = total_swap_mb - free_swap_mb;
186 if (verbose >= 3)
187 printf (_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb);
188 } else {
189 # endif
190 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
191 sscanf (input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
193 dsktotal_mb = dsktotal_mb / conv_factor;
194 /* AIX lists percent used, so this converts to dskfree in MBs */
195 # ifdef _AIX
196 dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
197 # else
198 dskfree_mb = dskfree_mb / conv_factor;
199 # endif
200 if (verbose >= 3)
201 printf (_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
203 dskused_mb = dsktotal_mb - dskfree_mb;
204 total_swap_mb += dsktotal_mb;
205 used_swap_mb += dskused_mb;
206 free_swap_mb += dskfree_mb;
207 if (allswaps) {
208 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
209 result = max_state (result, check_swap (percent, dskfree_mb));
210 if (verbose)
211 asprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
212 }
213 }
214 # ifdef _AIX
215 }
216 # endif
218 /* If we get anything on STDERR, at least set warning */
219 while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr))
220 result = max_state (result, STATE_WARNING);
222 /* close stderr */
223 (void) fclose (child_stderr);
225 /* close the pipe */
226 if (spclose (child_process))
227 result = max_state (result, STATE_WARNING);
228 # else
229 # ifdef CHECK_SWAP_SWAPCTL_SVR4
231 /* get the number of active swap devices */
232 nswaps=swapctl(SC_GETNSWP, NULL);
234 /* initialize swap table + entries */
235 tbl=(swaptbl_t*)malloc(sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps));
236 memset(tbl, 0, sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps));
237 tbl->swt_n=nswaps;
238 for(i=0;i<nswaps;i++){
239 ent=&tbl->swt_ent[i];
240 ent->ste_path=(char*)malloc(sizeof(char)*MAXPATHLEN);
241 }
243 /* and now, tally 'em up */
244 swapctl_res=swapctl(SC_LIST, tbl);
245 if(swapctl_res < 0){
246 perror(_("swapctl failed: "));
247 result = STATE_WARNING;
248 }
250 for(i=0;i<nswaps;i++){
251 dsktotal_mb = (float) tbl->swt_ent[i].ste_pages / SWAP_CONVERSION;
252 dskfree_mb = (float) tbl->swt_ent[i].ste_free / SWAP_CONVERSION;
253 dskused_mb = ( dsktotal_mb - dskfree_mb );
255 if (verbose >= 3)
256 printf ("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb);
258 if(allswaps && dsktotal_mb > 0){
259 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
260 result = max_state (result, check_swap (percent, dskfree_mb));
261 if (verbose) {
262 asprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
263 }
264 }
266 total_swap_mb += dsktotal_mb;
267 free_swap_mb += dskfree_mb;
268 used_swap_mb += dskused_mb;
269 }
271 /* and clean up after ourselves */
272 for(i=0;i<nswaps;i++){
273 free(tbl->swt_ent[i].ste_path);
274 }
275 free(tbl);
276 # else
277 # ifdef CHECK_SWAP_SWAPCTL_BSD
279 /* get the number of active swap devices */
280 nswaps=swapctl(SWAP_NSWAP, NULL, 0);
282 /* initialize swap table + entries */
283 ent=(struct swapent*)malloc(sizeof(struct swapent)*nswaps);
285 /* and now, tally 'em up */
286 swapctl_res=swapctl(SWAP_STATS, ent, nswaps);
287 if(swapctl_res < 0){
288 perror(_("swapctl failed: "));
289 result = STATE_WARNING;
290 }
292 for(i=0;i<nswaps;i++){
293 dsktotal_mb = (float) ent->se_nblks / conv_factor;
294 dskused_mb = (float) ent->se_inuse / conv_factor;
295 dskfree_mb = ( dsktotal_mb - dskused_mb );
297 if(allswaps && dsktotal_mb > 0){
298 percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
299 result = max_state (result, check_swap (percent, dskfree_mb));
300 if (verbose) {
301 asprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
302 }
303 }
305 total_swap_mb += dsktotal_mb;
306 free_swap_mb += dskfree_mb;
307 used_swap_mb += dskused_mb;
308 }
310 /* and clean up after ourselves */
311 free(ent);
313 # endif /* CHECK_SWAP_SWAPCTL_BSD */
314 # endif /* CHECK_SWAP_SWAPCTL_SVR4 */
315 # endif /* HAVE_SWAP */
316 #endif /* HAVE_PROC_MEMINFO */
318 /* if total_swap_mb == 0, let's not divide by 0 */
319 if(total_swap_mb) {
320 percent_used = 100 * ((double) used_swap_mb) / ((double) total_swap_mb);
321 } else {
322 percent_used = 0;
323 }
325 result = max_state (result, check_swap (percent_used, free_swap_mb));
326 printf (_("SWAP %s - %d%% free (%d MB out of %d MB) %s|"),
327 state_text (result),
328 (100 - percent_used), (int) free_swap_mb, (int) total_swap_mb, status);
330 puts (perfdata ("swap", (long) free_swap_mb, "MB",
331 TRUE, (long) max (warn_size_bytes/(1024 * 1024), warn_percent/100.0*total_swap_mb),
332 TRUE, (long) max (crit_size_bytes/(1024 * 1024), crit_percent/100.0*total_swap_mb),
333 TRUE, 0,
334 TRUE, (long) total_swap_mb));
336 return result;
337 }
341 int
342 check_swap (int usp, float free_swap_mb)
343 {
344 int result = STATE_UNKNOWN;
345 float free_swap = free_swap_mb * (1024 * 1024); /* Convert back to bytes as warn and crit specified in bytes */
346 if (usp >= 0 && crit_percent != 0 && usp >= (100.0 - crit_percent))
347 result = STATE_CRITICAL;
348 else if (crit_size_bytes > 0 && free_swap <= crit_size_bytes)
349 result = STATE_CRITICAL;
350 else if (usp >= 0 && warn_percent != 0 && usp >= (100.0 - warn_percent))
351 result = STATE_WARNING;
352 else if (warn_size_bytes > 0 && free_swap <= warn_size_bytes)
353 result = STATE_WARNING;
354 else if (usp >= 0.0)
355 result = STATE_OK;
356 return result;
357 }
361 /* process command-line arguments */
362 int
363 process_arguments (int argc, char **argv)
364 {
365 int c = 0; /* option character */
367 int option = 0;
368 static struct option longopts[] = {
369 {"warning", required_argument, 0, 'w'},
370 {"critical", required_argument, 0, 'c'},
371 {"allswaps", no_argument, 0, 'a'},
372 {"verbose", no_argument, 0, 'v'},
373 {"version", no_argument, 0, 'V'},
374 {"help", no_argument, 0, 'h'},
375 {0, 0, 0, 0}
376 };
378 if (argc < 2)
379 return ERROR;
381 while (1) {
382 c = getopt_long (argc, argv, "+?Vvhac:w:", longopts, &option);
384 if (c == -1 || c == EOF)
385 break;
387 switch (c) {
388 case 'w': /* warning size threshold */
389 if (is_intnonneg (optarg)) {
390 warn_size_bytes = (float) atoi (optarg);
391 break;
392 }
393 else if (strstr (optarg, ",") &&
394 strstr (optarg, "%") &&
395 sscanf (optarg, "%f,%d%%", &warn_size_bytes, &warn_percent) == 2) {
396 warn_size_bytes = floorf(warn_size_bytes);
397 break;
398 }
399 else if (strstr (optarg, "%") &&
400 sscanf (optarg, "%d%%", &warn_percent) == 1) {
401 break;
402 }
403 else {
404 usage4 (_("Warning threshold must be integer or percentage!"));
405 }
406 case 'c': /* critical size threshold */
407 if (is_intnonneg (optarg)) {
408 crit_size_bytes = (float) atoi (optarg);
409 break;
410 }
411 else if (strstr (optarg, ",") &&
412 strstr (optarg, "%") &&
413 sscanf (optarg, "%f,%d%%", &crit_size_bytes, &crit_percent) == 2) {
414 crit_size_bytes = floorf(crit_size_bytes);
415 break;
416 }
417 else if (strstr (optarg, "%") &&
418 sscanf (optarg, "%d%%", &crit_percent) == 1) {
419 break;
420 }
421 else {
422 usage4 (_("Critical threshold must be integer or percentage!"));
423 }
424 case 'a': /* all swap */
425 allswaps = TRUE;
426 break;
427 case 'v': /* verbose */
428 verbose++;
429 break;
430 case 'V': /* version */
431 print_revision (progname, revision);
432 exit (STATE_OK);
433 case 'h': /* help */
434 print_help ();
435 exit (STATE_OK);
436 case '?': /* error */
437 usage2 (_("Unknown argument"), optarg);
438 }
439 }
441 c = optind;
442 if (c == argc)
443 return validate_arguments ();
444 if (warn_percent == 0 && is_intnonneg (argv[c]))
445 warn_percent = atoi (argv[c++]);
447 if (c == argc)
448 return validate_arguments ();
449 if (crit_percent == 0 && is_intnonneg (argv[c]))
450 crit_percent = atoi (argv[c++]);
452 if (c == argc)
453 return validate_arguments ();
454 if (warn_size_bytes == 0 && is_intnonneg (argv[c]))
455 warn_size_bytes = (float) atoi (argv[c++]);
457 if (c == argc)
458 return validate_arguments ();
459 if (crit_size_bytes == 0 && is_intnonneg (argv[c]))
460 crit_size_bytes = (float) atoi (argv[c++]);
462 return validate_arguments ();
463 }
467 int
468 validate_arguments (void)
469 {
470 if (warn_percent == 0 && crit_percent == 0 && warn_size_bytes == 0
471 && crit_size_bytes == 0) {
472 return ERROR;
473 }
474 else if (warn_percent < crit_percent) {
475 usage4
476 (_("Warning percentage should be more than critical percentage"));
477 }
478 else if (warn_size_bytes < crit_size_bytes) {
479 usage4
480 (_("Warning free space should be more than critical free space"));
481 }
482 return OK;
483 }
487 void
488 print_help (void)
489 {
490 print_revision (progname, revision);
492 printf (_(COPYRIGHT), copyright, email);
494 printf (_("Check swap space on local machine.\n\n"));
496 print_usage ();
498 printf (_(UT_HELP_VRSN));
500 printf (_("\n\
501 -w, --warning=INTEGER\n\
502 Exit with WARNING status if less than INTEGER bytes of swap space are free\n\
503 -w, --warning=PERCENT%%\n\
504 Exit with WARNING status if less than PERCENT of swap space is free\n\
505 -c, --critical=INTEGER\n\
506 Exit with CRITICAL status if less than INTEGER bytes of swap space are free\n\
507 -c, --critical=PERCENT%%\n\
508 Exit with CRITCAL status if less than PERCENT of swap space is free\n\
509 -a, --allswaps\n\
510 Conduct comparisons for all swap partitions, one by one\n\
511 -v, --verbose\n\
512 Verbose output. Up to 3 levels\n"));
514 printf (_("\n\
515 On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s.\n"));
517 printf (_(UT_SUPPORT));
518 }
522 void
523 print_usage (void)
524 {
525 printf ("\
526 Usage: %s [-av] -w <percent_free>%% -c <percent_free>%%\n\
527 %s [-av] -w <bytes_free> -c <bytes_free>\n", progname, progname);
528 }