summaryrefslogtreecommitdiffstats
path: root/plugins/check_swap.d/swap.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_swap.d/swap.c')
-rw-r--r--plugins/check_swap.d/swap.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/plugins/check_swap.d/swap.c b/plugins/check_swap.d/swap.c
new file mode 100644
index 00000000..2fe4544f
--- /dev/null
+++ b/plugins/check_swap.d/swap.c
@@ -0,0 +1,469 @@
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#ifdef _AIX
18 tmp.on_aix = true;
19#else
20 tmp.on_aix = false;
21#endif
22
23 return tmp;
24}
25
26swap_result get_swap_data(swap_config config) {
27#ifdef HAVE_PROC_MEMINFO
28 if (verbose >= 3) {
29 printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO);
30 }
31
32 return getSwapFromProcMeminfo(PROC_MEMINFO);
33#else // HAVE_PROC_MEMINFO
34# ifdef HAVE_SWAP
35 if (verbose >= 3) {
36 printf("Using swap command %s with format: %s\n", SWAP_COMMAND, SWAP_FORMAT);
37 }
38
39 /* These override the command used if a summary (and thus ! allswaps) is
40 * required
41 * The summary flag returns more accurate information about swap usage on these
42 * OSes */
43 if (config.on_aix && !config.allswaps) {
44
45 config.conversion_factor = 1;
46
47 return getSwapFromSwapCommand(config, "/usr/sbin/lsps -s", "%lu%*s %lu");
48 } else {
49 return getSwapFromSwapCommand(config, SWAP_COMMAND, SWAP_FORMAT);
50 }
51# else // HAVE_SWAP
52# ifdef CHECK_SWAP_SWAPCTL_SVR4
53 return getSwapFromSwapctl_SRV4();
54# else // CHECK_SWAP_SWAPCTL_SVR4
55# ifdef CHECK_SWAP_SWAPCTL_BSD
56 return getSwapFromSwapctl_BSD();
57# else // CHECK_SWAP_SWAPCTL_BSD
58# error No way found to retrieve swap
59# endif /* CHECK_SWAP_SWAPCTL_BSD */
60# endif /* CHECK_SWAP_SWAPCTL_SVR4 */
61# endif /* HAVE_SWAP */
62#endif /* HAVE_PROC_MEMINFO */
63}
64
65swap_result getSwapFromProcMeminfo(char proc_meminfo[]) {
66 FILE *meminfo_file_ptr;
67 meminfo_file_ptr = fopen(proc_meminfo, "r");
68
69 swap_result result = {0};
70 result.errorcode = STATE_UNKNOWN;
71
72 if (meminfo_file_ptr == NULL) {
73 // failed to open meminfo file
74 // errno should contain an error
75 result.errorcode = STATE_UNKNOWN;
76 return result;
77 }
78
79 uint64_t swap_total = 0;
80 uint64_t swap_used = 0;
81 uint64_t swap_free = 0;
82
83 bool found_total = false;
84 bool found_used = false;
85 bool found_free = false;
86
87 char input_buffer[MAX_INPUT_BUFFER];
88 char str[32];
89
90 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) {
91 uint64_t tmp_KB = 0;
92
93 /*
94 * The following sscanf call looks for a line looking like: "Swap: 123
95 * 123 123" On which kind of system this format exists, I can not say,
96 * but I wanted to document this for people who are not adapt with
97 * sscanf anymore, like me
98 * Also the units used here are unclear and probably wrong
99 */
100 if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) {
101
102 result.metrics.total += swap_total;
103 result.metrics.used += swap_used;
104 result.metrics.free += swap_free;
105
106 found_total = true;
107 found_free = true;
108 found_used = true;
109
110 // Set error
111 result.errorcode = STATE_OK;
112
113 /*
114 * The following sscanf call looks for lines looking like:
115 * "SwapTotal: 123" and "SwapFree: 123" This format exists at least
116 * on Debian Linux with a 5.* kernel
117 */
118 } else {
119 int sscanf_result = sscanf(input_buffer,
120 "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu "
121 "%*[k]%*[B]",
122 str, &tmp_KB);
123
124 if (sscanf_result == 2) {
125
126 if (verbose >= 3) {
127 printf("Got %s with %lu\n", str, tmp_KB);
128 }
129
130 /* I think this part is always in Kb, so convert to bytes */
131 if (strcmp("Total", str) == 0) {
132 swap_total = tmp_KB * 1000;
133 found_total = true;
134 } else if (strcmp("Free", str) == 0) {
135 swap_free = swap_free + tmp_KB * 1000;
136 found_free = true;
137 found_used = true; // No explicit used metric available
138 } else if (strcmp("Cached", str) == 0) {
139 swap_free = swap_free + tmp_KB * 1000;
140 found_free = true;
141 found_used = true; // No explicit used metric available
142 }
143
144 result.errorcode = STATE_OK;
145 }
146 }
147 }
148
149 fclose(meminfo_file_ptr);
150
151 result.metrics.total = swap_total;
152 result.metrics.used = swap_total - swap_free;
153 result.metrics.free = swap_free;
154
155 if (!found_free || !found_total || !found_used) {
156 result.errorcode = STATE_UNKNOWN;
157 }
158
159 return result;
160}
161
162swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]) {
163 swap_result result = {0};
164
165 char *temp_buffer;
166
167 if (verbose >= 2) {
168 printf(_("Command: %s\n"), swap_command);
169 }
170 if (verbose >= 3) {
171 printf(_("Format: %s\n"), swap_format);
172 }
173
174 child_process = spopen(swap_command);
175 if (child_process == NULL) {
176 printf(_("Could not open pipe: %s\n"), swap_command);
177 swap_result tmp = {
178 .errorcode = STATE_UNKNOWN,
179 };
180 return tmp;
181 }
182
183 child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r");
184 if (child_stderr == NULL) {
185 printf(_("Could not open stderr for %s\n"), swap_command);
186 }
187
188 char str[32] = {0};
189 char input_buffer[MAX_INPUT_BUFFER];
190
191 /* read 1st line */
192 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process);
193 if (strcmp(swap_format, "") == 0) {
194 temp_buffer = strtok(input_buffer, " \n");
195 while (temp_buffer) {
196 if (strstr(temp_buffer, "blocks")) {
197 sprintf(str, "%s %s", str, "%lu");
198 } else if (strstr(temp_buffer, "dskfree")) {
199 sprintf(str, "%s %s", str, "%lu");
200 } else {
201 sprintf(str, "%s %s", str, "%*s");
202 }
203 temp_buffer = strtok(NULL, " \n");
204 }
205 }
206
207 double total_swap_mb = 0;
208 double free_swap_mb = 0;
209 double used_swap_mb = 0;
210 double dsktotal_mb = 0;
211 double dskused_mb = 0;
212 double dskfree_mb = 0;
213
214 /*
215 * If different swap command is used for summary switch, need to read format
216 * differently
217 */
218 if (config.on_aix && !config.allswaps) {
219 fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */
220 sscanf(input_buffer, swap_format, &total_swap_mb, &used_swap_mb);
221 free_swap_mb = total_swap_mb * (100 - used_swap_mb) / 100;
222 used_swap_mb = total_swap_mb - free_swap_mb;
223
224 if (verbose >= 3) {
225 printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb);
226 }
227 } else {
228 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
229 sscanf(input_buffer, swap_format, &dsktotal_mb, &dskfree_mb);
230
231 dsktotal_mb = dsktotal_mb / config.conversion_factor;
232 /* AIX lists percent used, so this converts to dskfree in MBs */
233
234 if (config.on_aix) {
235 dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100;
236 } else {
237 dskfree_mb = dskfree_mb / config.conversion_factor;
238 }
239
240 if (verbose >= 3) {
241 printf(_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb);
242 }
243
244 dskused_mb = dsktotal_mb - dskfree_mb;
245 total_swap_mb += dsktotal_mb;
246 used_swap_mb += dskused_mb;
247 free_swap_mb += dskfree_mb;
248 }
249 }
250
251 result.metrics.free = free_swap_mb * 1024 * 1024;
252 result.metrics.used = used_swap_mb * 1024 * 1024;
253 result.metrics.total = free_swap_mb * 1024 * 1024;
254
255 /* If we get anything on STDERR, at least set warning */
256 while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
257 result.statusCode = max_state(result.statusCode, STATE_WARNING);
258 // TODO Set error here
259 }
260
261 /* close stderr */
262 (void)fclose(child_stderr);
263
264 /* close the pipe */
265 if (spclose(child_process)) {
266 result.statusCode = max_state(result.statusCode, STATE_WARNING);
267 // TODO set error here
268 }
269
270 return result;
271}
272
273#ifndef CHECK_SWAP_SWAPCTL_BSD
274# define CHECK_SWAP_SWAPCTL_BSD
275
276// Stub functionality for BSD stuff, so the compiler always sees the following BSD code
277
278# define SWAP_NSWAP 0
279# define SWAP_STATS 1
280
281int bsd_swapctl(int cmd, const void *arg, int misc) {
282 (void)cmd;
283 (void)arg;
284 (void)misc;
285 return 512;
286}
287
288struct swapent {
289 dev_t se_dev; /* device id */
290 int se_flags; /* entry flags */
291 int se_nblks; /* total blocks */
292 int se_inuse; /* blocks in use */
293 int se_priority; /* priority */
294 char se_path[PATH_MAX]; /* path to entry */
295};
296
297#else
298# define bsd_swapctl swapctl
299#endif
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 = (float)ent[i].se_nblks / (float)config.conversion_factor;
324 dskused_mb = (float)ent[i].se_inuse / (float)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 = (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
406
407 if (tbl == NULL) {
408 die(STATE_UNKNOWN, _("malloc() failed!\n"));
409 }
410
411 memset(tbl, 0, sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps));
412 tbl->swt_n = nswaps;
413
414 for (int i = 0; i < nswaps; i++) {
415 if ((tbl->swt_ent[i].ste_path = (char *)malloc(sizeof(char) * MAXPATHLEN)) == NULL) {
416 die(STATE_UNKNOWN, _("malloc() failed!\n"));
417 }
418 }
419
420 /* and now, tally 'em up */
421 int swapctl_res = srv4_swapctl(SC_LIST, tbl);
422 if (swapctl_res < 0) {
423 perror(_("swapctl failed: "));
424 die(STATE_UNKNOWN, _("Error in swapctl call\n"));
425 }
426
427 double dsktotal_mb = 0.0;
428 double dskfree_mb = 0.0;
429 double dskused_mb = 0.0;
430 unsigned long long total_swap_mb = 0;
431 unsigned long long free_swap_mb = 0;
432 unsigned long long used_swap_mb = 0;
433
434 for (int i = 0; i < nswaps; i++) {
435 dsktotal_mb = (float)tbl->swt_ent[i].ste_pages / SWAP_CONVERSION;
436 dskfree_mb = (float)tbl->swt_ent[i].ste_free / SWAP_CONVERSION;
437 dskused_mb = (dsktotal_mb - dskfree_mb);
438
439 if (verbose >= 3) {
440 printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb);
441 }
442
443 if (config.allswaps && dsktotal_mb > 0) {
444 double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb));
445
446 if (verbose) {
447 printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent);
448 }
449 }
450
451 total_swap_mb += (unsigned long long)dsktotal_mb;
452 free_swap_mb += (unsigned long long)dskfree_mb;
453 used_swap_mb += (unsigned long long)dskused_mb;
454 }
455
456 /* and clean up after ourselves */
457 for (int i = 0; i < nswaps; i++) {
458 free(tbl->swt_ent[i].ste_path);
459 }
460 free(tbl);
461
462 swap_result result = {0};
463 result.errorcode = OK;
464 result.metrics.total = total_swap_mb * 1024 * 1024;
465 result.metrics.free = free_swap_mb * 1024 * 1024;
466 result.metrics.used = used_swap_mb * 1024 * 1024;
467
468 return result;
469}