summaryrefslogtreecommitdiffstats
path: root/plugins/check_swap.d
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_swap.d')
-rw-r--r--plugins/check_swap.d/check_swap.h47
-rw-r--r--plugins/check_swap.d/swap.c471
2 files changed, 518 insertions, 0 deletions
diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h
new file mode 100644
index 00000000..1000fc9e
--- /dev/null
+++ b/plugins/check_swap.d/check_swap.h
@@ -0,0 +1,47 @@
1#pragma once
2
3#include "../common.h"
4#include "output.h"
5
6#ifndef SWAP_CONVERSION
7# define SWAP_CONVERSION 1
8#endif
9
10typedef struct {
11 bool is_percentage;
12 uint64_t value;
13} check_swap_threshold;
14
15typedef struct {
16 unsigned long long free; // Free swap in Bytes!
17 unsigned long long used; // Used swap in Bytes!
18 unsigned long long total; // Total swap size, you guessed it, in Bytes!
19} swap_metrics;
20
21typedef struct {
22 int errorcode;
23 int statusCode;
24 swap_metrics metrics;
25} swap_result;
26
27typedef struct {
28 bool allswaps;
29 int no_swap_state;
30 bool warn_is_set;
31 check_swap_threshold warn;
32 bool crit_is_set;
33 check_swap_threshold crit;
34 bool on_aix;
35 int conversion_factor;
36
37 bool output_format_is_set;
38 mp_output_format output_format;
39} swap_config;
40
41swap_config swap_config_init(void);
42
43swap_result get_swap_data(swap_config config);
44swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]);
45swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]);
46swap_result getSwapFromSwapctl_BSD(swap_config config);
47swap_result getSwapFromSwap_SRV4(swap_config config);
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c
new file mode 100644
index 00000000..180d5037
--- /dev/null
+++ b/plugins/check_swap.d/swap.c
@@ -0,0 +1,471 @@
1#include "./check_swap.d/check_swap.h"
2#include "../popen.h"
3#include "../utils.h"
4#include "common.h"
5
6extern int verbose;
7
8swap_config swap_config_init(void) {
9 swap_config tmp = {0};
10 tmp.allswaps = false;
11 tmp.no_swap_state = STATE_CRITICAL;
12 tmp.conversion_factor = SWAP_CONVERSION;
13
14 tmp.warn_is_set = false;
15 tmp.crit_is_set = false;
16
17 tmp.output_format_is_set = false;
18
19#ifdef _AIX
20 tmp.on_aix = true;
21#else
22 tmp.on_aix = false;
23#endif
24
25 return tmp;
26}
27
28swap_result get_swap_data(swap_config config) {
29#ifdef HAVE_PROC_MEMINFO
30 if (verbose >= 3) {
31 printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
32 }
33
34 return getSwapFromProcMeminfo(PROC_MEMINFO);
35#else // HAVE_PROC_MEMINFO
36# ifdef HAVE_SWAP
37 if (verbose >= 3) {
38 printf("Using swap command %s with format: %s\n", SWAP_COMMAND, SWAP_FORMAT);
39 }
40
41 /* These override the command used if a summary (and thus ! allswaps) is
42 * required
43 * The summary flag returns more accurate information about swap usage on these
44 * OSes */
45 if (config.on_aix && !config.allswaps) {
46
47 config.conversion_factor = 1;
48
49 return getSwapFromSwapCommand(config, "/usr/sbin/lsps -s", "%lu%*s %lu");
50 } else {
51 return getSwapFromSwapCommand(config, SWAP_COMMAND, SWAP_FORMAT);
52 }
53# else // HAVE_SWAP
54# ifdef CHECK_SWAP_SWAPCTL_SVR4
55 return getSwapFromSwapctl_SRV4();
56# else // CHECK_SWAP_SWAPCTL_SVR4
57# ifdef CHECK_SWAP_SWAPCTL_BSD
58 return getSwapFromSwapctl_BSD();
59# else // CHECK_SWAP_SWAPCTL_BSD
60# error No way found to retrieve swap
61# endif /* CHECK_SWAP_SWAPCTL_BSD */
62# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
63# endif /* HAVE_SWAP */
64#endif /* HAVE_PROC_MEMINFO */
65}
66
67swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
68 FILE *meminfo_file_ptr;
69 meminfo_file_ptr = fopen(proc_meminfo, "r");
70
71 swap_result result = {0};
72 result.errorcode = STATE_UNKNOWN;
73
74 if (meminfo_file_ptr == NULL) {
75 // failed to open meminfo file
76 // errno should contain an error
77 result.errorcode = STATE_UNKNOWN;
78 return result;
79 }
80
81 uint64_t swap_total = 0;
82 uint64_t swap_used = 0;
83 uint64_t swap_free = 0;
84
85 bool found_total = false;
86 bool found_used = false;
87 bool found_free = false;
88
89 char input_buffer[MAX_INPUT_BUFFER];
90 char str[32];
91
92 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) {
93 uint64_t tmp_KB = 0;
94
95 /*
96 * The following sscanf call looks for a line looking like: "Swap: 123
97 * 123 123" On which kind of system this format exists, I can not say,
98 * but I wanted to document this for people who are not adapt with
99 * sscanf anymore, like me
100 * Also the units used here are unclear and probably wrong
101 */
102 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) {
103
104 result.metrics.total += swap_total;
105 result.metrics.used += swap_used;
106 result.metrics.free += swap_free;
107
108 found_total = true;
109 found_free = true;
110 found_used = true;
111
112 // Set error
113 result.errorcode = STATE_OK;
114
115 /*
116 * The following sscanf call looks for lines looking like:
117 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least
118 * on Debian Linux with a 5.* kernel
119 */
120 } else {
121 int sscanf_result = sscanf(input_buffer,
122 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu "
123 "%*[k]%*[B]",
124 str, &tmp_KB);
125
126 if (sscanf_result == 2) {
127
128 if (verbose >= 3) {
129 printf("Got %s with %lu\n", str, tmp_KB);
130 }
131
132 /* I think this part is always in Kb, so convert to bytes */
133 if (strcmp("Total", str) == 0) {
134 swap_total = tmp_KB * 1000;
135 found_total = true;
136 } else if (strcmp("Free", str) == 0) {
137 swap_free = swap_free + tmp_KB * 1000;
138 found_free = true;
139 found_used = true; // No explicit used metric available
140 } else if (strcmp("Cached", str) == 0) {
141 swap_free = swap_free + tmp_KB * 1000;
142 found_free = true;
143 found_used = true; // No explicit used metric available
144 }
145
146 result.errorcode = STATE_OK;
147 }
148 }
149 }
150
151 fclose(meminfo_file_ptr);
152
153 result.metrics.total = swap_total;
154 result.metrics.used = swap_total - swap_free;
155 result.metrics.free = swap_free;
156
157 if (!found_free || !found_total || !found_used) {
158 result.errorcode = STATE_UNKNOWN;
159 }
160
161 return result;
162}
163
164swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]) {
165 swap_result result = {0};
166
167 char *temp_buffer;
168
169 if (verbose >= 2) {
170 printf(_("Command: %s\n"), swap_command);
171 }
172 if (verbose >= 3) {
173 printf(_("Format: %s\n"), swap_format);
174 }
175
176 child_process = spopen(swap_command);
177 if (child_process == NULL) {
178 printf(_("Could not open pipe: %s\n"), swap_command);
179 swap_result tmp = {
180 .errorcode = STATE_UNKNOWN,
181 };
182 return tmp;
183 }
184
185 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
186 if (child_stderr == NULL) {
187 printf(_("Could not open stderr for %s\n"), swap_command);
188 }
189
190 char str[32] = {0};
191 char input_buffer[MAX_INPUT_BUFFER];
192
193 /* read 1st line */
194 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process);
195 if (strcmp(swap_format, "") == 0) {
196 temp_buffer = strtok(input_buffer, " \n");
197 while (temp_buffer) {
198 if (strstr(temp_buffer, "blocks")) {
199 sprintf(str, "%s %s", str, "%lu");
200 } else if (strstr(temp_buffer, "dskfree")) {
201 sprintf(str, "%s %s", str, "%lu");
202 } else {
203 sprintf(str, "%s %s", str, "%*s");
204 }
205 temp_buffer = strtok(NULL, " \n");
206 }
207 }
208
209 double total_swap_mb = 0;
210 double free_swap_mb = 0;
211 double used_swap_mb = 0;
212 double dsktotal_mb = 0;
213 double dskused_mb = 0;
214 double dskfree_mb = 0;
215
216 /*
217 * If different swap command is used for summary switch, need to read format
218 * differently
219 */
220 if (config.on_aix && !config.allswaps) {
221 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */
222 sscanf(input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
223 free_swap_mb = total_swap_mb * (100 - used_swap_mb) / 100;
224 used_swap_mb = total_swap_mb - free_swap_mb;
225
226 if (verbose >= 3) {
227 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb);
228 }
229 } else {
230 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
231 sscanf(input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
232
233 dsktotal_mb = dsktotal_mb / config.conversion_factor;
234 /* AIX lists percent used, so this converts to dskfree in MBs */
235
236 if (config.on_aix) {
237 dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
238 } else {
239 dskfree_mb = dskfree_mb / config.conversion_factor;
240 }
241
242 if (verbose >= 3) {
243 printf(_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
244 }
245
246 dskused_mb = dsktotal_mb - dskfree_mb;
247 total_swap_mb += dsktotal_mb;
248 used_swap_mb += dskused_mb;
249 free_swap_mb += dskfree_mb;
250 }
251 }
252
253 result.metrics.free = free_swap_mb * 1024 * 1024;
254 result.metrics.used = used_swap_mb * 1024 * 1024;
255 result.metrics.total = free_swap_mb * 1024 * 1024;
256
257 /* If we get anything on STDERR, at least set warning */
258 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
259 result.statusCode = max_state(result.statusCode, STATE_WARNING);
260 // TODO Set error here
261 }
262
263 /* close stderr */
264 (void)fclose(child_stderr);
265
266 /* close the pipe */
267 if (spclose(child_process)) {
268 result.statusCode = max_state(result.statusCode, STATE_WARNING);
269 // TODO set error here
270 }
271
272 return result;
273}
274
275#ifndef CHECK_SWAP_SWAPCTL_BSD
276# define CHECK_SWAP_SWAPCTL_BSD
277
278// Stub functionality for BSD stuff, so the compiler always sees the following BSD code
279
280# define SWAP_NSWAP 0
281# define SWAP_STATS 1
282
283int bsd_swapctl(int cmd, const void *arg, int misc) {
284 (void)cmd;
285 (void)arg;
286 (void)misc;
287 return 512;
288}
289
290struct swapent {
291 dev_t se_dev; /* device id */
292 int se_flags; /* entry flags */
293 int se_nblks; /* total blocks */
294 int se_inuse; /* blocks in use */
295 int se_priority; /* priority */
296 char se_path[PATH_MAX]; /* path to entry */
297};
298
299#else
300# define bsd_swapctl swapctl
301#endif
302
303swap_result getSwapFromSwapctl_BSD(swap_config config) {
304 /* get the number of active swap devices */
305 int nswaps = bsd_swapctl(SWAP_NSWAP, NULL, 0);
306
307 /* initialize swap table + entries */
308 struct swapent *ent = (struct swapent *)malloc(sizeof(struct swapent) * (unsigned long)nswaps);
309
310 /* and now, tally 'em up */
311 int swapctl_res = bsd_swapctl(SWAP_STATS, ent, nswaps);
312 if (swapctl_res < 0) {
313 perror(_("swapctl failed: "));
314 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
315 }
316
317 double dsktotal_mb = 0.0;
318 double dskfree_mb = 0.0;
319 double dskused_mb = 0.0;
320 unsigned long long total_swap_mb = 0;
321 unsigned long long free_swap_mb = 0;
322 unsigned long long used_swap_mb = 0;
323
324 for (int i = 0; i < nswaps; i++) {
325 dsktotal_mb = (float)ent[i].se_nblks / (float)config.conversion_factor;
326 dskused_mb = (float)ent[i].se_inuse / (float)config.conversion_factor;
327 dskfree_mb = (dsktotal_mb - dskused_mb);
328
329 if (config.allswaps && dsktotal_mb > 0) {
330 double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb));
331
332 if (verbose) {
333 printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent);
334 }
335 }
336
337 total_swap_mb += (unsigned long long)dsktotal_mb;
338 free_swap_mb += (unsigned long long)dskfree_mb;
339 used_swap_mb += (unsigned long long)dskused_mb;
340 }
341
342 /* and clean up after ourselves */
343 free(ent);
344
345 swap_result result = {0};
346
347 result.statusCode = OK;
348 result.errorcode = OK;
349
350 result.metrics.total = total_swap_mb * 1024 * 1024;
351 result.metrics.free = free_swap_mb * 1024 * 1024;
352 result.metrics.used = used_swap_mb * 1024 * 1024;
353
354 return result;
355}
356
357#ifndef CHECK_SWAP_SWAPCTL_SVR4
358int srv4_swapctl(int cmd, void *arg) {
359 (void)cmd;
360 (void)arg;
361 return 512;
362}
363
364typedef struct srv4_swapent {
365 char *ste_path; /* name of the swap file */
366 off_t ste_start; /* starting block for swapping */
367 off_t ste_length; /* length of swap area */
368 long ste_pages; /* number of pages for swapping */
369 long ste_free; /* number of ste_pages free */
370 long ste_flags; /* ST_INDEL bit set if swap file */
371 /* is now being deleted */
372} swapent_t;
373
374typedef struct swaptbl {
375 int swt_n; /* number of swapents following */
376 struct srv4_swapent swt_ent[]; /* array of swt_n swapents */
377} swaptbl_t;
378
379# define SC_LIST 2
380# define SC_GETNSWP 3
381
382# ifndef MAXPATHLEN
383# define MAXPATHLEN 2048
384# endif
385
386#else
387# define srv4_swapctl swapctl
388#endif
389
390swap_result getSwapFromSwap_SRV4(swap_config config) {
391 int nswaps = 0;
392
393 /* get the number of active swap devices */
394 if ((nswaps = srv4_swapctl(SC_GETNSWP, NULL)) == -1) {
395 die(STATE_UNKNOWN, _("Error getting swap devices\n"));
396 }
397
398 if (nswaps == 0) {
399 die(STATE_OK, _("SWAP OK: No swap devices defined\n"));
400 }
401
402 if (verbose >= 3) {
403 printf("Found %d swap device(s)\n", nswaps);
404 }
405
406 /* initialize swap table + entries */
407 swaptbl_t *tbl = (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
408
409 if (tbl == NULL) {
410 die(STATE_UNKNOWN, _("malloc() failed!\n"));
411 }
412
413 memset(tbl, 0, sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
414 tbl->swt_n = nswaps;
415
416 for (int i = 0; i < nswaps; i++) {
417 if ((tbl->swt_ent[i].ste_path = (char *)malloc(sizeof(char) * MAXPATHLEN)) == NULL) {
418 die(STATE_UNKNOWN, _("malloc() failed!\n"));
419 }
420 }
421
422 /* and now, tally 'em up */
423 int swapctl_res = srv4_swapctl(SC_LIST, tbl);
424 if (swapctl_res < 0) {
425 perror(_("swapctl failed: "));
426 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
427 }
428
429 double dsktotal_mb = 0.0;
430 double dskfree_mb = 0.0;
431 double dskused_mb = 0.0;
432 unsigned long long total_swap_mb = 0;
433 unsigned long long free_swap_mb = 0;
434 unsigned long long used_swap_mb = 0;
435
436 for (int i = 0; i < nswaps; i++) {
437 dsktotal_mb = (float)tbl->swt_ent[i].ste_pages / SWAP_CONVERSION;
438 dskfree_mb = (float)tbl->swt_ent[i].ste_free / SWAP_CONVERSION;
439 dskused_mb = (dsktotal_mb - dskfree_mb);
440
441 if (verbose >= 3) {
442 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb);
443 }
444
445 if (config.allswaps && dsktotal_mb > 0) {
446 double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb));
447
448 if (verbose) {
449 printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent);
450 }
451 }
452
453 total_swap_mb += (unsigned long long)dsktotal_mb;
454 free_swap_mb += (unsigned long long)dskfree_mb;
455 used_swap_mb += (unsigned long long)dskused_mb;
456 }
457
458 /* and clean up after ourselves */
459 for (int i = 0; i < nswaps; i++) {
460 free(tbl->swt_ent[i].ste_path);
461 }
462 free(tbl);
463
464 swap_result result = {0};
465 result.errorcode = OK;
466 result.metrics.total = total_swap_mb * 1024 * 1024;
467 result.metrics.free = free_swap_mb * 1024 * 1024;
468 result.metrics.used = used_swap_mb * 1024 * 1024;
469
470 return result;
471}