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