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