1 /*****************************************************************************
2 *
3 * Nagios check_swap plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net)
7 * Copyright (c) 2000-2014 Nagios Plugins Development Team
8 *
9 * Description:
10 *
11 * This file contains the check_swap plugin
12 *
13 *
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 *
27 *
28 *****************************************************************************/
29 
30 const char *progname = "check_swap";
31 const char *copyright = "2000-2014";
32 const char *email = "devel@nagios-plugins.org";
33 
34 #include "common.h"
35 #include "popen.h"
36 #include "utils.h"
37 
38 #ifdef HAVE_DECL_SWAPCTL
39 # ifdef HAVE_SYS_PARAM_H
40 #  include <sys/param.h>
41 # endif
42 # ifdef HAVE_SYS_SWAP_H
43 #  include <sys/swap.h>
44 # endif
45 # ifdef HAVE_SYS_STAT_H
46 #  include <sys/stat.h>
47 # endif
48 #endif
49 
50 #ifndef SWAP_CONVERSION
51 # define SWAP_CONVERSION 1
52 #endif
53 
54 int check_swap (int usp, double free_swap_mb);
55 int process_arguments (int argc, char **argv);
56 int validate_arguments (void);
57 void print_usage (void);
58 void print_help (void);
59 
60 int have_warn = 0;
61 int have_crit = 0;
62 int warn_percent = 0;
63 int crit_percent = 0;
64 double warn_size_bytes = 0;
65 double crit_size_bytes= 0;
66 int verbose;
67 int allswaps;
68 int no_swap_state = STATE_CRITICAL;
69 
70 int
main(int argc,char ** argv)71 main (int argc, char **argv)
72 {
73 	int percent_used, percent;
74 	double total_swap_mb = 0, used_swap_mb = 0, free_swap_mb = 0;
75 	double dsktotal_mb = 0, dskused_mb = 0, dskfree_mb = 0, tmp_mb = 0;
76 	int result = STATE_UNKNOWN;
77 	char input_buffer[MAX_INPUT_BUFFER];
78 #ifdef HAVE_PROC_MEMINFO
79 	FILE *fp;
80 #else
81 	int conv_factor = SWAP_CONVERSION;
82 # ifdef HAVE_SWAP
83 	char *temp_buffer;
84 	char *swap_command;
85 	char *swap_format;
86 # else
87 #  ifdef HAVE_DECL_SWAPCTL
88 	int i=0, nswaps=0, swapctl_res=0;
89 #   ifdef CHECK_SWAP_SWAPCTL_SVR4
90 	swaptbl_t *tbl=NULL;
91 	swapent_t *ent=NULL;
92 #   else
93 #    ifdef CHECK_SWAP_SWAPCTL_BSD
94 	struct swapent *ent;
95 #    endif /* CHECK_SWAP_SWAPCTL_BSD */
96 #   endif /* CHECK_SWAP_SWAPCTL_SVR4 */
97 #  endif /* HAVE_DECL_SWAPCTL */
98 # endif
99 #endif
100 	char str[32];
101 	char *status;
102 
103 	setlocale (LC_ALL, ""); setlocale(LC_NUMERIC, "C");
104 	bindtextdomain (PACKAGE, LOCALEDIR);
105 	textdomain (PACKAGE);
106 
107 	status = strdup ("");
108 
109 	/* Parse extra opts if any */
110 	argv=np_extra_opts (&argc, argv, progname);
111 
112 	if (process_arguments (argc, argv) == ERROR)
113 		usage4 (_("Could not parse arguments"));
114 
115 #ifdef HAVE_PROC_MEMINFO
116 	if (verbose >= 3) {
117 		printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
118 	}
119 	fp = fopen (PROC_MEMINFO, "r");
120 	while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) {
121 		if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lf %lf %lf", &dsktotal_mb, &dskused_mb, &dskfree_mb) == 3) {
122 			dsktotal_mb = dsktotal_mb / 1048576;	/* Apply conversion */
123 			dskused_mb = dskused_mb / 1048576;
124 			dskfree_mb = dskfree_mb / 1048576;
125 			total_swap_mb += dsktotal_mb;
126 			used_swap_mb += dskused_mb;
127 			free_swap_mb += dskfree_mb;
128 			if (allswaps) {
129 				if (dsktotal_mb == 0)
130 					percent=0.0;
131 				else
132 					percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
133 				result = max_state (result, check_swap (percent, dskfree_mb));
134 				if (verbose)
135 					xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
136 			}
137 		}
138 		else if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%[TotalFre]%*[:] %lf %*[k]%*[B]", str, &tmp_mb)) {
139 			if (verbose >= 3) {
140 				printf("Got %s with %lf\n", str, tmp_mb);
141 			}
142 			/* I think this part is always in Kb, so convert to mb */
143 			if (strcmp ("Total", str) == 0) {
144 				dsktotal_mb = tmp_mb / 1024;
145 			}
146 			else if (strcmp ("Free", str) == 0) {
147 				dskfree_mb = tmp_mb / 1024;
148 			}
149 		}
150 	}
151 	fclose(fp);
152 	dskused_mb = dsktotal_mb - dskfree_mb;
153 	total_swap_mb = dsktotal_mb;
154 	used_swap_mb = dskused_mb;
155 	free_swap_mb = dskfree_mb;
156 #else
157 # ifdef HAVE_SWAP
158 	xasprintf(&swap_command, "%s", SWAP_COMMAND);
159 	xasprintf(&swap_format, "%s", SWAP_FORMAT);
160 
161 /* These override the command used if a summary (and thus ! allswaps) is required */
162 /* The summary flag returns more accurate information about swap usage on these OSes */
163 #  ifdef _AIX
164 	if (!allswaps) {
165 		xasprintf(&swap_command, "%s", "/usr/sbin/lsps -s");
166 		xasprintf(&swap_format, "%s", "%lf%*s %lf");
167 		conv_factor = 1;
168 	}
169 #  endif
170 
171 	if (verbose >= 2)
172 		printf (_("Command: %s\n"), swap_command);
173 	if (verbose >= 3)
174 		printf (_("Format: %s\n"), swap_format);
175 
176 	child_process = spopen (swap_command);
177 	if (child_process == NULL) {
178 		printf (_("Could not open pipe: %s\n"), swap_command);
179 		return STATE_UNKNOWN;
180 	}
181 
182 	child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
183 	if (child_stderr == NULL)
184 		printf (_("Could not open stderr for %s\n"), swap_command);
185 
186 	sprintf (str, "%s", "");
187 	/* read 1st line */
188 	fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process);
189 	if (strcmp (swap_format, "") == 0) {
190 		temp_buffer = strtok (input_buffer, " \n");
191 		while (temp_buffer) {
192 			if (strstr (temp_buffer, "blocks"))
193 				sprintf (str, "%s %s", str, "%lf");
194 			else if (strstr (temp_buffer, "dskfree"))
195 				sprintf (str, "%s %s", str, "%lf");
196 			else
197 				sprintf (str, "%s %s", str, "%*s");
198 			temp_buffer = strtok (NULL, " \n");
199 		}
200 	}
201 
202 /* If different swap command is used for summary switch, need to read format differently */
203 #  ifdef _AIX
204 	if (!allswaps) {
205 		fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process);	/* Ignore first line */
206 		sscanf (input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
207 		free_swap_mb = total_swap_mb * (100 - used_swap_mb) /100;
208 		used_swap_mb = total_swap_mb - free_swap_mb;
209 		if (verbose >= 3)
210 			printf (_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb);
211 	} else {
212 #  endif
213 		while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
214 			sscanf (input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
215 
216 			dsktotal_mb = dsktotal_mb / conv_factor;
217 			/* AIX lists percent used, so this converts to dskfree in MBs */
218 #  ifdef _AIX
219 			dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
220 #  else
221 			dskfree_mb = dskfree_mb / conv_factor;
222 #  endif
223 			if (verbose >= 3)
224 				printf (_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
225 
226 			dskused_mb = dsktotal_mb - dskfree_mb;
227 			total_swap_mb += dsktotal_mb;
228 			used_swap_mb += dskused_mb;
229 			free_swap_mb += dskfree_mb;
230 			if (allswaps) {
231 				percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
232 				result = max_state (result, check_swap (percent, dskfree_mb));
233 				if (verbose)
234 					xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
235 			}
236 		}
237 #  ifdef _AIX
238 	}
239 #  endif
240 
241 	/* If we get anything on STDERR, at least set warning */
242 	while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr))
243 		result = max_state (result, STATE_WARNING);
244 
245 	/* close stderr */
246 	(void) fclose (child_stderr);
247 
248 	/* close the pipe */
249 	if (spclose (child_process))
250 		result = max_state (result, STATE_WARNING);
251 # else
252 #  ifdef CHECK_SWAP_SWAPCTL_SVR4
253 
254 	/* get the number of active swap devices */
255 	if((nswaps=swapctl(SC_GETNSWP, NULL))== -1)
256 		die(STATE_UNKNOWN, _("Error getting swap devices\n") );
257 
258 	if(nswaps == 0)
259 		die(STATE_OK, _("SWAP OK: No swap devices defined\n"));
260 
261 	if(verbose >= 3)
262 		printf("Found %d swap device(s)\n", nswaps);
263 
264 	/* initialize swap table + entries */
265 	tbl=(swaptbl_t*)malloc(sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps));
266 
267 	if(tbl==NULL)
268 		die(STATE_UNKNOWN, _("malloc() failed!\n"));
269 
270 	memset(tbl, 0, sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps));
271 	tbl->swt_n=nswaps;
272 	for(i=0;i<nswaps;i++){
273 		if((tbl->swt_ent[i].ste_path=(char*)malloc(sizeof(char)*MAXPATHLEN)) == NULL)
274 			die(STATE_UNKNOWN, _("malloc() failed!\n"));
275 	}
276 
277 	/* and now, tally 'em up */
278 	swapctl_res=swapctl(SC_LIST, tbl);
279 	if(swapctl_res < 0){
280 		perror(_("swapctl failed: "));
281 		die(STATE_UNKNOWN, _("Error in swapctl call\n"));
282 	}
283 
284 	for(i=0;i<nswaps;i++){
285 		dsktotal_mb = (double) tbl->swt_ent[i].ste_pages / SWAP_CONVERSION;
286 		dskfree_mb = (double) tbl->swt_ent[i].ste_free /  SWAP_CONVERSION;
287 		dskused_mb = ( dsktotal_mb - dskfree_mb );
288 
289 		if (verbose >= 3)
290 			printf ("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb);
291 
292 		if(allswaps && dsktotal_mb > 0){
293 			percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
294 			result = max_state (result, check_swap (percent, dskfree_mb));
295 			if (verbose) {
296 				xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
297 			}
298 		}
299 
300 		total_swap_mb += dsktotal_mb;
301 		free_swap_mb += dskfree_mb;
302 		used_swap_mb += dskused_mb;
303 	}
304 
305 	/* and clean up after ourselves */
306 	for(i=0;i<nswaps;i++){
307 		free(tbl->swt_ent[i].ste_path);
308 	}
309 	free(tbl);
310 #  else
311 #   ifdef CHECK_SWAP_SWAPCTL_BSD
312 
313 	/* get the number of active swap devices */
314 	nswaps=swapctl(SWAP_NSWAP, NULL, 0);
315 
316 	/* initialize swap table + entries */
317 	ent=(struct swapent*)malloc(sizeof(struct swapent)*nswaps);
318 
319 	/* and now, tally 'em up */
320 	swapctl_res=swapctl(SWAP_STATS, ent, nswaps);
321 	if(swapctl_res < 0){
322 		perror(_("swapctl failed: "));
323 		die(STATE_UNKNOWN, _("Error in swapctl call\n"));
324 	}
325 
326 	for(i=0;i<nswaps;i++){
327 		dsktotal_mb = (double) ent[i].se_nblks / conv_factor;
328 		dskused_mb = (double) ent[i].se_inuse / conv_factor;
329 		dskfree_mb = ( dsktotal_mb - dskused_mb );
330 
331 		if(allswaps && dsktotal_mb > 0){
332 			percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb));
333 			result = max_state (result, check_swap (percent, dskfree_mb));
334 			if (verbose) {
335 				xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent);
336 			}
337 		}
338 
339 		total_swap_mb += dsktotal_mb;
340 		free_swap_mb += dskfree_mb;
341 		used_swap_mb += dskused_mb;
342 	}
343 
344 	/* and clean up after ourselves */
345 	free(ent);
346 
347 #   endif /* CHECK_SWAP_SWAPCTL_BSD */
348 #  endif /* CHECK_SWAP_SWAPCTL_SVR4 */
349 # endif /* HAVE_SWAP */
350 #endif /* HAVE_PROC_MEMINFO */
351 
352 	/* if total_swap_mb == 0, swap is most likely missing or disabled and should go critical */
353 	if(total_swap_mb == 0) {
354 		percent_used = 100;
355 		status = "- Swap is either disabled, not present, or of zero size. ";
356 	} else if(total_swap_mb > 0) {
357 		percent_used = 100 * ((double) used_swap_mb) / ((double) total_swap_mb);
358 	} else {
359 		percent_used = 100;
360 		status = "- Swap is either disabled, not present, or of zero size. ";
361 	}
362 
363 	result = max_state (result, check_swap (percent_used, free_swap_mb));
364 	printf (_("SWAP %s - %d%% free (%d MB out of %d MB) %s|"),
365 			state_text (result),
366 			(100 - percent_used), (int) free_swap_mb, (int) total_swap_mb, status);
367 
368 	puts (perfdata ("swap", (long) free_swap_mb, "MB",
369 	                TRUE, (long) max (warn_size_bytes/(1024 * 1024), warn_percent/100.0*total_swap_mb),
370 	                TRUE, (long) max (crit_size_bytes/(1024 * 1024), crit_percent/100.0*total_swap_mb),
371 	                TRUE, 0,
372 	                TRUE, (long) total_swap_mb));
373 
374 	return result;
375 }
376 
377 
378 
379 int
check_swap(int usp,double free_swap_mb)380 check_swap (int usp, double free_swap_mb)
381 {
382 	if (!free_swap_mb) return no_swap_state;
383 
384 	int result = STATE_UNKNOWN;
385 	double free_swap = free_swap_mb * (1024 * 1024);		/* Convert back to bytes as warn and crit specified in bytes */
386 	if (usp >= 0 && crit_percent != 0 && usp >= (100.0 - crit_percent))
387 		result = STATE_CRITICAL;
388 	else if (crit_size_bytes > 0 && free_swap <= crit_size_bytes)
389 		result = STATE_CRITICAL;
390 	else if (usp >= 0 && warn_percent != 0 && usp >= (100.0 - warn_percent))
391 		result = STATE_WARNING;
392 	else if (warn_size_bytes > 0 && free_swap <= warn_size_bytes)
393 		result = STATE_WARNING;
394 	else if (usp >= 0.0)
395 		result = STATE_OK;
396 	return result;
397 }
398 
399 
400 
401 /* process command-line arguments */
402 int
process_arguments(int argc,char ** argv)403 process_arguments (int argc, char **argv)
404 {
405 	int c = 0;  /* option character */
406 
407 	int option = 0;
408 	static struct option longopts[] = {
409 		{"warning", required_argument, 0, 'w'},
410 		{"critical", required_argument, 0, 'c'},
411 		{"allswaps", no_argument, 0, 'a'},
412 		{"no-swap", required_argument, 0, 'n'},
413 		{"verbose", no_argument, 0, 'v'},
414 		{"version", no_argument, 0, 'V'},
415 		{"help", no_argument, 0, 'h'},
416 		{0, 0, 0, 0}
417 	};
418 
419 	if (argc < 2)
420 		return ERROR;
421 
422 	while (1) {
423 		c = getopt_long (argc, argv, "+?Vvhac:w:n:", longopts, &option);
424 
425 		if (c == -1 || c == EOF)
426 			break;
427 
428 		switch (c) {
429 		case 'w':									/* warning size threshold */
430 			if (is_intnonneg (optarg)) {
431 				warn_size_bytes = (double) atoi (optarg);
432 				have_warn = TRUE;
433 				break;
434 			}
435 			else if (strstr (optarg, ",") &&
436 							 strstr (optarg, "%") &&
437 							 sscanf (optarg, "%lf,%d%%", &warn_size_bytes, &warn_percent) == 2) {
438 				warn_size_bytes = floorf(warn_size_bytes);
439 				have_warn = TRUE;
440 				break;
441 			}
442 			else if (strstr (optarg, "%") &&
443 							 sscanf (optarg, "%d%%", &warn_percent) == 1) {
444 				have_warn = TRUE;
445 				break;
446 			}
447 			else {
448 				usage4 (_("Warning threshold must be integer or percentage!"));
449 			}
450 		case 'c':									/* critical size threshold */
451 			if (is_intnonneg (optarg)) {
452 				crit_size_bytes = (double) atoi (optarg);
453 				have_crit = TRUE;
454 				break;
455 			}
456 			else if (strstr (optarg, ",") &&
457 							 strstr (optarg, "%") &&
458 							 sscanf (optarg, "%lf,%d%%", &crit_size_bytes, &crit_percent) == 2) {
459 				crit_size_bytes = floorf(crit_size_bytes);
460 				have_crit = TRUE;
461 				break;
462 			}
463 			else if (strstr (optarg, "%") &&
464 							 sscanf (optarg, "%d%%", &crit_percent) == 1) {
465 				have_crit = TRUE;
466 				break;
467 			}
468 			else {
469 				usage4 (_("Critical threshold must be integer or percentage!"));
470 			}
471 		case 'a':									/* all swap */
472 			allswaps = TRUE;
473 			break;
474 		case 'n':									/* no-swap */
475 			if ((no_swap_state = translate_state(optarg)) == ERROR) {
476 				usage4 (_("no-swap result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3)."));
477 			}
478 		case 'v':									/* verbose */
479 			verbose++;
480 			break;
481 		case 'V':									/* version */
482 			print_revision (progname, NP_VERSION);
483 			exit (STATE_OK);
484 		case 'h':									/* help */
485 			print_help ();
486 			exit (STATE_OK);
487 		case '?':									/* error */
488 			usage5 ();
489 		}
490 	}
491 
492 	c = optind;
493 	if (c == argc)
494 		return validate_arguments ();
495 	if (warn_percent == 0 && is_intnonneg (argv[c]))
496 		warn_percent = atoi (argv[c++]);
497 
498 	if (c == argc)
499 		return validate_arguments ();
500 	if (crit_percent == 0 && is_intnonneg (argv[c]))
501 		crit_percent = atoi (argv[c++]);
502 
503 	if (c == argc)
504 		return validate_arguments ();
505 	if (warn_size_bytes == 0 && is_intnonneg (argv[c]))
506 		warn_size_bytes = (double) atoi (argv[c++]);
507 
508 	if (c == argc)
509 		return validate_arguments ();
510 	if (crit_size_bytes == 0 && is_intnonneg (argv[c]))
511 		crit_size_bytes = (double) atoi (argv[c++]);
512 
513 	return validate_arguments ();
514 }
515 
516 
517 
518 int
validate_arguments(void)519 validate_arguments (void)
520 {
521 	if (have_crit == FALSE && have_warn == FALSE)
522 		return ERROR;
523 	else if (warn_percent < 0 || crit_percent < 0 || warn_size_bytes < 0
524 			|| crit_size_bytes < 0) {
525 		return ERROR;
526 	}
527 	else if (warn_percent < crit_percent) {
528 		usage4
529 			(_("Warning percentage should be more than critical percentage"));
530 	}
531 	else if (warn_size_bytes < crit_size_bytes) {
532 		usage4
533 			(_("Warning free space should be more than critical free space"));
534 	}
535 	return OK;
536 }
537 
538 
539 
540 void
print_help(void)541 print_help (void)
542 {
543   print_revision (progname, NP_VERSION);
544 
545   printf (_(COPYRIGHT), copyright, email);
546 
547   printf ("%s\n", _("Check swap space on local machine."));
548 
549   printf ("\n\n");
550 
551   print_usage ();
552 
553   printf (UT_HELP_VRSN);
554   printf (UT_EXTRA_OPTS);
555 
556   printf (" %s\n", "-w, --warning=INTEGER");
557   printf ("    %s\n", _("Exit with WARNING status if less than INTEGER bytes of swap space are free"));
558   printf (" %s\n", "-w, --warning=PERCENT%%");
559   printf ("    %s\n", _("Exit with WARNING status if less than PERCENT of swap space is free"));
560   printf (" %s\n", "-c, --critical=INTEGER");
561   printf ("    %s\n", _("Exit with CRITICAL status if less than INTEGER bytes of swap space are free"));
562   printf (" %s\n", "-c, --critical=PERCENT%%");
563   printf ("    %s\n", _("Exit with CRITICAL status if less than PERCENT of swap space is free"));
564   printf (" %s\n", "-a, --allswaps");
565   printf ("    %s\n", _("Conduct comparisons for all swap partitions, one by one"));
566   printf (" %s\n", "-n, --no-swap=<ok|warning|critical|unknown>");
567   printf ("    %s %s\n", _("Resulting state when there is no swap regardless of thresholds. Default:"), state_text(no_swap_state));
568 
569   printf (UT_VERBOSE);
570 
571   printf ("\n");
572   printf ("%s\n", _("Notes:"));
573   printf (" %s\n", _("Both INTEGER and PERCENT thresholds can be specified, they are all checked."));
574   printf (" %s\n", _("On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s."));
575 
576   printf (UT_SUPPORT);
577 }
578 
579 
580 void
print_usage(void)581 print_usage (void)
582 {
583   printf ("%s\n", _("Usage:"));
584   printf (" %s [-av] -w <percent_free>%% -c <percent_free>%%\n",progname);
585   printf ("  -w <bytes_free> -c <bytes_free> [-n <state>]\n");
586 }
587