diff --git a/.gitignore b/.gitignore index 6f903d61..bbd7d844 100644 --- a/.gitignore +++ b/.gitignore @@ -221,7 +221,13 @@ NP-VERSION-FILE /plugins/tests/Makefile.in /plugins/tests/test_utils /plugins/tests/test_disk +/plugins/tests/test_check_swap /plugins/tests/.deps +/plugins/tests/.dirstamp + +# /plugins/check_swap.d +/plugins/check_swap.d/.deps +/plugins/check_swap.d/.dirstamp # /plugins-root/ /plugins-root/.deps diff --git a/configure.ac b/configure.ac index 8594238f..0432336b 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_INIT(monitoring-plugins,2.4git) AC_CONFIG_SRCDIR(NPTest.pm) AC_CONFIG_FILES([gl/Makefile]) AC_CONFIG_AUX_DIR(build-aux) -AM_INIT_AUTOMAKE([1.8.3]) +AM_INIT_AUTOMAKE([1.8.3 subdir-objects]) AM_SILENT_RULES([yes]) AM_MAINTAINER_MODE([enable]) AC_CONFIG_HEADERS([config.h]) @@ -185,6 +185,9 @@ fi if test "$enable_libtap" = "yes" ; then EXTRA_TEST="test_utils test_disk test_tcp test_cmd test_base64" AC_SUBST(EXTRA_TEST) + + EXTRA_PLUGIN_TESTS="tests/test_check_swap" + AC_SUBST(EXTRA_PLUGIN_TESTS) fi dnl INI Parsing @@ -1850,8 +1853,8 @@ AC_SUBST(EXTRAS_ROOT) AC_SUBST(EXTRA_NETOBJS) AC_SUBST(DEPLIBS) -AM_GNU_GETTEXT([external], [need-ngettext]) -AM_GNU_GETTEXT_VERSION(0.15) +dnl AM_GNU_GETTEXT([external], [need-ngettext]) +dnl AM_GNU_GETTEXT_VERSION(0.15) dnl Check for Redhat spopen problem dnl Weird problem where ECHILD is returned from a wait call in error diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 49086b7a..d43c1971 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -38,19 +38,27 @@ check_tcp_programs = check_ftp check_imap check_nntp check_pop \ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ check_swap check_fping check_ldap check_game check_dig \ check_nagios check_by_ssh check_dns check_nt check_ide_smart \ - check_procs check_mysql_query check_apt check_dbi check_curl + check_procs check_mysql_query check_apt check_dbi check_curl \ + \ + tests/test_check_swap SUBDIRS = picohttpparser -EXTRA_DIST = t tests +np_test_scripts = tests/test_check_swap.t + +EXTRA_DIST = t tests $(np_test_scripts) check_swap.d PLUGINHDRS = common.h noinst_LIBRARIES = libnpcommon.a +noinst_PROGRAMS = @EXTRA_PLUGIN_TESTS@ +# These two lines support "make check", but we use "make test" +check_PROGRAMS = @EXTRA_PLUGIN_TESTS@ libnpcommon_a_SOURCES = utils.c netutils.c sslutils.c runcmd.c \ popen.c utils.h netutils.h popen.h common.h runcmd.c runcmd.h + BASEOBJS = libnpcommon.a ../lib/libmonitoringplug.a ../gl/libgnu.a $(LIB_CRYPTO) NETOBJS = $(BASEOBJS) $(EXTRA_NETOBLS) NETLIBS = $(NETOBJS) $(SOCKETLIBS) @@ -58,7 +66,10 @@ SSLOBJS = $(BASEOBJS) $(NETLIBS) $(SSLLIBS) $(LIB_CRYPTO) TESTS_ENVIRONMENT = perl -I $(top_builddir) -I $(top_srcdir) -TESTS = @PLUGIN_TEST@ +tap_ldflags = -L$(top_srcdir)/tap + +TESTS = @PLUGIN_TEST@ @EXTRA_PLUGIN_TESTS@ + test: perl -I $(top_builddir) -I $(top_srcdir) ../test.pl @@ -107,6 +118,7 @@ check_real_LDADD = $(NETLIBS) check_snmp_LDADD = $(BASEOBJS) check_smtp_LDADD = $(SSLOBJS) check_ssh_LDADD = $(NETLIBS) +check_swap_SOURCES = check_swap.c check_swap.d/swap.c check_swap_LDADD = $(MATHLIBS) $(BASEOBJS) check_tcp_LDADD = $(SSLOBJS) check_time_LDADD = $(NETLIBS) @@ -122,6 +134,9 @@ if !HAVE_UTMPX check_users_LDADD += popen.o endif +tests_test_check_swap_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap +tests_test_check_swap_SOURCES = tests/test_check_swap.c check_swap.d/swap.c + ############################################################################## # secondary dependencies diff --git a/plugins/check_swap.c b/plugins/check_swap.c index e7ee785d..bc90a90b 100644 --- a/plugins/check_swap.c +++ b/plugins/check_swap.c @@ -1,607 +1,337 @@ /***************************************************************************** -* -* Monitoring check_swap plugin -* -* License: GPL -* Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) -* Copyright (c) 2000-2024 Monitoring Plugins Development Team -* -* Description: -* -* This file contains the check_swap plugin -* -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -* -*****************************************************************************/ + * + * Monitoring check_swap plugin + * + * License: GPL + * Copyright (c) 2000 Karl DeBisschop (kdebisschop@users.sourceforge.net) + * Copyright (c) 2000-2024 Monitoring Plugins Development Team + * + * Description: + * + * This file contains the check_swap plugin + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + *****************************************************************************/ + +#include "common.h" +#ifdef HAVE_DECL_SWAPCTL +# ifdef HAVE_SYS_PARAM_H +# include +# endif +# ifdef HAVE_SYS_SWAP_H +# include +# endif +# ifdef HAVE_SYS_STAT_H +# include +# endif +#endif + +#include +#include "./check_swap.d/check_swap.h" +#include "./utils.h" + +typedef struct { + int errorcode; + swap_config config; +} swap_config_wrapper; + +static swap_config_wrapper process_arguments(int argc, char **argv); +void print_usage(void); +static void print_help(swap_config /*config*/); + +int verbose; + +#define HUNDRED_PERCENT 100 + +#define BYTES_TO_KiB(number) (number / 1024) +#define BYTES_TO_MiB(number) (BYTES_TO_KiB(number) / 1024) const char *progname = "check_swap"; const char *copyright = "2000-2024"; const char *email = "devel@monitoring-plugins.org"; -#include "common.h" -#include "popen.h" -#include "utils.h" +int main(int argc, char **argv) { + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); -#ifdef HAVE_DECL_SWAPCTL -# ifdef HAVE_SYS_PARAM_H -# include -# endif -# ifdef HAVE_SYS_SWAP_H -# include -# endif -# ifdef HAVE_SYS_STAT_H -# include -# endif -#endif - -#ifndef SWAP_CONVERSION -# define SWAP_CONVERSION 1 -#endif - -typedef struct { - bool is_percentage; - uint64_t value; -} threshold; - -int check_swap (float free_swap_mb, float total_swap_mb); -int process_arguments (int argc, char **argv); -int validate_arguments (void); -void print_usage (void); -void print_help (void); - -threshold warn; -threshold crit; -int verbose; -bool allswaps = false; -int no_swap_state = STATE_CRITICAL; - -int -main (int argc, char **argv) -{ - unsigned int percent_used, percent; - uint64_t total_swap_mb = 0, used_swap_mb = 0, free_swap_mb = 0; - uint64_t dsktotal_mb = 0, dskused_mb = 0, dskfree_mb = 0; - uint64_t tmp_KB = 0; - int result = STATE_UNKNOWN; - char input_buffer[MAX_INPUT_BUFFER]; -#ifdef HAVE_PROC_MEMINFO - FILE *fp; -#else - int conv_factor = SWAP_CONVERSION; -# ifdef HAVE_SWAP - char *temp_buffer; - char *swap_command; - char *swap_format; -# else -# ifdef HAVE_DECL_SWAPCTL - int i=0, nswaps=0, swapctl_res=0; -# ifdef CHECK_SWAP_SWAPCTL_SVR4 - swaptbl_t *tbl=NULL; - swapent_t *ent=NULL; -# else -# ifdef CHECK_SWAP_SWAPCTL_BSD - struct swapent *ent; -# endif /* CHECK_SWAP_SWAPCTL_BSD */ -# endif /* CHECK_SWAP_SWAPCTL_SVR4 */ -# endif /* HAVE_DECL_SWAPCTL */ -# endif -#endif - char str[32]; - char *status; - - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); - - status = strdup (""); + char *status = strdup(""); /* Parse extra opts if any */ - argv=np_extra_opts (&argc, argv, progname); + argv = np_extra_opts(&argc, argv, progname); - if (process_arguments (argc, argv) == ERROR) - usage4 (_("Could not parse arguments")); + swap_config_wrapper tmp = process_arguments(argc, argv); -#ifdef HAVE_PROC_MEMINFO - if (verbose >= 3) { - printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO); - } - fp = fopen (PROC_MEMINFO, "r"); - while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, fp)) { - /* - * The following sscanf call looks for a line looking like: "Swap: 123 123 123" - * On which kind of system this format exists, I can not say, but I wanted to - * document this for people who are not adapt with sscanf anymore, like me - */ - if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &dsktotal_mb, &dskused_mb, &dskfree_mb) == 3) { - dsktotal_mb = dsktotal_mb / (1024 * 1024); /* Apply conversion */ - dskused_mb = dskused_mb / (1024 * 1024); - dskfree_mb = dskfree_mb / (1024 * 1024); - total_swap_mb += dsktotal_mb; - used_swap_mb += dskused_mb; - free_swap_mb += dskfree_mb; - if (allswaps) { - if (dsktotal_mb == 0) - percent=100.0; - else - percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb)); - result = max_state (result, check_swap (dskfree_mb, dsktotal_mb)); - if (verbose) - xasprintf (&status, "%s [%lu (%d%%)]", status, dskfree_mb, 100 - percent); - } - } - - /* - * The following sscanf call looks for lines looking like: "SwapTotal: 123" and "SwapFree: 123" - * This format exists at least on Debian Linux with a 5.* kernel - */ - else if (sscanf (input_buffer, "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu %*[k]%*[B]", str, &tmp_KB)) { - if (verbose >= 3) { - printf("Got %s with %lu\n", str, tmp_KB); - } - /* I think this part is always in Kb, so convert to mb */ - if (strcmp ("Total", str) == 0) { - dsktotal_mb = tmp_KB / 1024; - } - else if (strcmp ("Free", str) == 0) { - dskfree_mb = dskfree_mb + tmp_KB / 1024; - } - else if (strcmp ("Cached", str) == 0) { - dskfree_mb = dskfree_mb + tmp_KB / 1024; - } - } - } - fclose(fp); - dskused_mb = dsktotal_mb - dskfree_mb; - total_swap_mb = dsktotal_mb; - used_swap_mb = dskused_mb; - free_swap_mb = dskfree_mb; -#else -# ifdef HAVE_SWAP - xasprintf(&swap_command, "%s", SWAP_COMMAND); - xasprintf(&swap_format, "%s", SWAP_FORMAT); - -/* These override the command used if a summary (and thus ! allswaps) is required */ -/* The summary flag returns more accurate information about swap usage on these OSes */ -# ifdef _AIX - if (!allswaps) { - xasprintf(&swap_command, "%s", "/usr/sbin/lsps -s"); - xasprintf(&swap_format, "%s", "%lu%*s %lu"); - conv_factor = 1; - } -# endif - - if (verbose >= 2) - printf (_("Command: %s\n"), swap_command); - if (verbose >= 3) - printf (_("Format: %s\n"), swap_format); - - child_process = spopen (swap_command); - if (child_process == NULL) { - printf (_("Could not open pipe: %s\n"), swap_command); - return STATE_UNKNOWN; + if (tmp.errorcode != OK) { + usage4(_("Could not parse arguments")); } - child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); - if (child_stderr == NULL) - printf (_("Could not open stderr for %s\n"), swap_command); + swap_config config = tmp.config; - sprintf (str, "%s", ""); - /* read 1st line */ - fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process); - if (strcmp (swap_format, "") == 0) { - temp_buffer = strtok (input_buffer, " \n"); - while (temp_buffer) { - if (strstr (temp_buffer, "blocks")) - sprintf (str, "%s %s", str, "%lu"); - else if (strstr (temp_buffer, "dskfree")) - sprintf (str, "%s %s", str, "%lu"); - else - sprintf (str, "%s %s", str, "%*s"); - temp_buffer = strtok (NULL, " \n"); - } + swap_result data = get_swap_data(config); + + if (data.errorcode != STATE_OK) { + puts("SWAP UNKNOWN - Failed to retrieve Swap usage"); + exit(STATE_UNKNOWN); } -/* If different swap command is used for summary switch, need to read format differently */ -# ifdef _AIX - if (!allswaps) { - fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */ - sscanf (input_buffer, swap_format, &total_swap_mb, &used_swap_mb); - free_swap_mb = total_swap_mb * (100 - used_swap_mb) /100; - used_swap_mb = total_swap_mb - free_swap_mb; - if (verbose >= 3) - printf (_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb); - } else { -# endif - while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { - sscanf (input_buffer, swap_format, &dsktotal_mb, &dskfree_mb); - - dsktotal_mb = dsktotal_mb / conv_factor; - /* AIX lists percent used, so this converts to dskfree in MBs */ -# ifdef _AIX - dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100; -# else - dskfree_mb = dskfree_mb / conv_factor; -# endif - if (verbose >= 3) - printf (_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb); - - dskused_mb = dsktotal_mb - dskfree_mb; - total_swap_mb += dsktotal_mb; - used_swap_mb += dskused_mb; - free_swap_mb += dskfree_mb; - if (allswaps) { - percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb)); - result = max_state (result, check_swap (dskfree_mb, dsktotal_mb)); - if (verbose) - xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent); - } - } -# ifdef _AIX - } -# endif - - /* If we get anything on STDERR, at least set warning */ - while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) - result = max_state (result, STATE_WARNING); - - /* close stderr */ - (void) fclose (child_stderr); - - /* close the pipe */ - if (spclose (child_process)) - result = max_state (result, STATE_WARNING); -# else -# ifdef CHECK_SWAP_SWAPCTL_SVR4 - - /* get the number of active swap devices */ - if((nswaps=swapctl(SC_GETNSWP, NULL))== -1) - die(STATE_UNKNOWN, _("Error getting swap devices\n") ); - - if(nswaps == 0) - die(STATE_OK, _("SWAP OK: No swap devices defined\n")); - - if(verbose >= 3) - printf("Found %d swap device(s)\n", nswaps); - - /* initialize swap table + entries */ - tbl=(swaptbl_t*)malloc(sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps)); - - if(tbl==NULL) - die(STATE_UNKNOWN, _("malloc() failed!\n")); - - memset(tbl, 0, sizeof(swaptbl_t)+(sizeof(swapent_t)*nswaps)); - tbl->swt_n=nswaps; - for(i=0;iswt_ent[i].ste_path=(char*)malloc(sizeof(char)*MAXPATHLEN)) == NULL) - die(STATE_UNKNOWN, _("malloc() failed!\n")); - } - - /* and now, tally 'em up */ - swapctl_res=swapctl(SC_LIST, tbl); - if(swapctl_res < 0){ - perror(_("swapctl failed: ")); - die(STATE_UNKNOWN, _("Error in swapctl call\n")); - } - - for(i=0;iswt_ent[i].ste_pages / SWAP_CONVERSION; - dskfree_mb = (float) tbl->swt_ent[i].ste_free / SWAP_CONVERSION; - dskused_mb = ( dsktotal_mb - dskfree_mb ); - - if (verbose >= 3) - printf ("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb); - - if(allswaps && dsktotal_mb > 0){ - percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb)); - result = max_state (result, check_swap (dskfree_mb, dsktotal_mb)); - if (verbose) { - xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent); - } - } - - total_swap_mb += dsktotal_mb; - free_swap_mb += dskfree_mb; - used_swap_mb += dskused_mb; - } - - /* and clean up after ourselves */ - for(i=0;iswt_ent[i].ste_path); - } - free(tbl); -# else -# ifdef CHECK_SWAP_SWAPCTL_BSD - - /* get the number of active swap devices */ - nswaps=swapctl(SWAP_NSWAP, NULL, 0); - - /* initialize swap table + entries */ - ent=(struct swapent*)malloc(sizeof(struct swapent)*nswaps); - - /* and now, tally 'em up */ - swapctl_res=swapctl(SWAP_STATS, ent, nswaps); - if(swapctl_res < 0){ - perror(_("swapctl failed: ")); - die(STATE_UNKNOWN, _("Error in swapctl call\n")); - } - - for(i=0;i 0){ - percent = 100 * (((double) dskused_mb) / ((double) dsktotal_mb)); - result = max_state (result, check_swap(dskfree_mb, dsktotal_mb)); - if (verbose) { - xasprintf (&status, "%s [%.0f (%d%%)]", status, dskfree_mb, 100 - percent); - } - } - - total_swap_mb += dsktotal_mb; - free_swap_mb += dskfree_mb; - used_swap_mb += dskused_mb; - } - - /* and clean up after ourselves */ - free(ent); - -# endif /* CHECK_SWAP_SWAPCTL_BSD */ -# endif /* CHECK_SWAP_SWAPCTL_SVR4 */ -# endif /* HAVE_SWAP */ -#endif /* HAVE_PROC_MEMINFO */ - + double percent_used; /* if total_swap_mb == 0, let's not divide by 0 */ - if(total_swap_mb) { - percent_used = 100 * ((double) used_swap_mb) / ((double) total_swap_mb); + if (data.metrics.total != 0) { + percent_used = HUNDRED_PERCENT * ((double)data.metrics.used) / ((double)data.metrics.total); } else { - percent_used = 100; - status = "- Swap is either disabled, not present, or of zero size. "; + printf(_("SWAP %s - Swap is either disabled, not present, or of zero " + "size."), + state_text(data.statusCode)); + exit(config.no_swap_state); } - result = max_state (result, check_swap(free_swap_mb, total_swap_mb)); - printf (_("SWAP %s - %d%% free (%dMB out of %dMB) %s|"), - state_text (result), - (100 - percent_used), (int) free_swap_mb, (int) total_swap_mb, status); + if (verbose) { + printf("Computed usage percentage: %g\n", percent_used); + } - uint64_t warn_print = warn.value; - if (warn.is_percentage) warn_print = warn.value * (total_swap_mb *1024 *1024/100); - uint64_t crit_print = crit.value; - if (crit.is_percentage) crit_print = crit.value * (total_swap_mb *1024 *1024/100); + uint64_t warn_print = config.warn.value; + if (config.warn.is_percentage) { + warn_print = config.warn.value * (data.metrics.total / HUNDRED_PERCENT); + } - puts (perfdata_uint64 ("swap", free_swap_mb *1024 *1024, "B", - true, warn_print, - true, crit_print, - true, 0, - true, (long) total_swap_mb * 1024 * 1024)); + uint64_t crit_print = config.crit.value; + if (config.crit.is_percentage) { + crit_print = config.crit.value * (data.metrics.total / HUNDRED_PERCENT); + } - return result; + char *perfdata = perfdata_uint64("swap", data.metrics.free, "B", config.warn_is_set, warn_print, config.crit_is_set, crit_print, true, + 0, true, data.metrics.total); + + if (config.warn_is_set) { + if (verbose > 1) { + printf("Warn threshold value: %" PRIu64 "\n", config.warn.value); + } + + if ((config.warn.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.warn.value))) || + config.warn.value >= data.metrics.free) { + data.statusCode = max_state(data.statusCode, STATE_WARNING); + } + } + + if (config.crit_is_set) { + if (verbose > 1) { + printf("Crit threshold value: %" PRIu64 "\n", config.crit.value); + } + + if ((config.crit.is_percentage && (percent_used >= (double)(HUNDRED_PERCENT - config.crit.value))) || + config.crit.value >= data.metrics.free) { + data.statusCode = max_state(data.statusCode, STATE_CRITICAL); + } + } + + printf(_("SWAP %s - %g%% free (%lluMiB out of %lluMiB) %s|%s\n"), state_text(data.statusCode), (HUNDRED_PERCENT - percent_used), + BYTES_TO_MiB(data.metrics.free), BYTES_TO_MiB(data.metrics.total), status, perfdata); + + exit(data.statusCode); } +int check_swap(float free_swap_mb, float total_swap_mb, swap_config config) { + if (total_swap_mb == 0) { + return config.no_swap_state; + } -int -check_swap(float free_swap_mb, float total_swap_mb) -{ + uint64_t free_swap = (uint64_t)(free_swap_mb * (1024 * 1024)); /* Convert back to bytes as warn and crit specified in bytes */ - if (!total_swap_mb) return no_swap_state; + if (!config.crit.is_percentage && config.crit.value >= free_swap) { + return STATE_CRITICAL; + } + if (!config.warn.is_percentage && config.warn.value >= free_swap) { + return STATE_WARNING; + } - uint64_t free_swap = free_swap_mb * (1024 * 1024); /* Convert back to bytes as warn and crit specified in bytes */ - uint64_t usage_percentage = ((total_swap_mb - free_swap_mb) / total_swap_mb) * 100; + uint64_t usage_percentage = (uint64_t)((total_swap_mb - free_swap_mb) / total_swap_mb) * HUNDRED_PERCENT; - if (warn.value || crit.value) { /* Thresholds defined */ - if (!crit.is_percentage && crit.value >= free_swap) return STATE_CRITICAL; - if (!warn.is_percentage && warn.value >= free_swap) return STATE_WARNING; + if (config.crit.is_percentage && config.crit.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.crit.value)) { + return STATE_CRITICAL; + } - if (crit.is_percentage && - crit.value != 0 && - usage_percentage >= (100 - crit.value)) - { - return STATE_CRITICAL; - } + if (config.warn.is_percentage && config.warn.value != 0 && usage_percentage >= (HUNDRED_PERCENT - config.warn.value)) { + return STATE_WARNING; + } - if (warn.is_percentage && - warn.value != 0 && - usage_percentage >= (100 - warn.value)) - { - return STATE_WARNING; - } - - return STATE_OK; - } else { /* Without thresholds */ - return STATE_OK; - } + return STATE_OK; } - - /* process command-line arguments */ -int -process_arguments (int argc, char **argv) -{ - int c = 0; /* option character */ +swap_config_wrapper process_arguments(int argc, char **argv) { + swap_config_wrapper conf_wrapper = {.errorcode = OK}; + conf_wrapper.config = swap_config_init(); - int option = 0; - static struct option longopts[] = { - {"warning", required_argument, 0, 'w'}, - {"critical", required_argument, 0, 'c'}, - {"allswaps", no_argument, 0, 'a'}, - {"no-swap", required_argument, 0, 'n'}, - {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0} - }; + static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, + {"allswaps", no_argument, 0, 'a'}, {"no-swap", required_argument, 0, 'n'}, + {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; - while (1) { - c = getopt_long (argc, argv, "+?Vvhac:w:n:", longopts, &option); + while (true) { + int option = 0; + int option_char = getopt_long(argc, argv, "+?Vvhac:w:n:", longopts, &option); - if (c == -1 || c == EOF) + if (option_char == -1 || option_char == EOF) { break; + } - switch (c) { + switch (option_char) { case 'w': /* warning size threshold */ - { - /* - * We expect either a positive integer value without a unit, which means - * the unit is Bytes or a positive integer value and a percentage sign (%), - * which means the value must be with 0 and 100 and is relative to the total swap - */ - size_t length; - length = strlen(optarg); + { + /* + * We expect either a positive integer value without a unit, which + * means the unit is Bytes or a positive integer value and a + * percentage sign (%), which means the value must be with 0 and 100 + * and is relative to the total swap + */ + size_t length; + length = strlen(optarg); + conf_wrapper.config.warn_is_set = true; - if (optarg[length - 1] == '%') { - /* It's percentage */ - warn.is_percentage = true; - optarg[length - 1] = '\0'; - if (is_uint64(optarg, &warn.value)) { - if (warn.value > 100) { - usage4 (_("Warning threshold percentage must be <= 100!")); - } - } - break; - } else { - /* It's Bytes */ - warn.is_percentage = false; - if (is_uint64(optarg, &warn.value)) { - break; - } else { - usage4 (_("Warning threshold be positive integer or percentage!")); + if (optarg[length - 1] == '%') { + /* It's percentage */ + conf_wrapper.config.warn.is_percentage = true; + optarg[length - 1] = '\0'; + if (is_uint64(optarg, &conf_wrapper.config.warn.value)) { + if (conf_wrapper.config.warn.value > HUNDRED_PERCENT) { + usage4(_("Warning threshold percentage must be <= 100!")); } } + break; + } /* It's Bytes */ + conf_wrapper.config.warn.is_percentage = false; + if (is_uint64(optarg, &conf_wrapper.config.warn.value)) { + break; } + usage4(_("Warning threshold be positive integer or " + "percentage!")); + } case 'c': /* critical size threshold */ - { - /* - * We expect either a positive integer value without a unit, which means - * the unit is Bytes or a positive integer value and a percentage sign (%), - * which means the value must be with 0 and 100 and is relative to the total swap - */ - size_t length; - length = strlen(optarg); + { + /* + * We expect either a positive integer value without a unit, which + * means the unit is Bytes or a positive integer value and a + * percentage sign (%), which means the value must be with 0 and 100 + * and is relative to the total swap + */ + size_t length; + length = strlen(optarg); + conf_wrapper.config.crit_is_set = true; - if (optarg[length - 1] == '%') { - /* It's percentage */ - crit.is_percentage = true; - optarg[length - 1] = '\0'; - if (is_uint64(optarg, &crit.value)) { - if (crit.value> 100) { - usage4 (_("Critical threshold percentage must be <= 100!")); - } - } - break; - } else { - /* It's Bytes */ - crit.is_percentage = false; - if (is_uint64(optarg, &crit.value)) { - break; - } else { - usage4 (_("Critical threshold be positive integer or percentage!")); + if (optarg[length - 1] == '%') { + /* It's percentage */ + conf_wrapper.config.crit.is_percentage = true; + optarg[length - 1] = '\0'; + if (is_uint64(optarg, &conf_wrapper.config.crit.value)) { + if (conf_wrapper.config.crit.value > HUNDRED_PERCENT) { + usage4(_("Critical threshold percentage must be <= 100!")); } } - } - case 'a': /* all swap */ - allswaps = true; + break; + } /* It's Bytes */ + conf_wrapper.config.crit.is_percentage = false; + if (is_uint64(optarg, &conf_wrapper.config.crit.value)) { + break; + } + usage4(_("Critical threshold be positive integer or " + "percentage!")); + } + case 'a': /* all swap */ + conf_wrapper.config.allswaps = true; break; case 'n': - if ((no_swap_state = mp_translate_state(optarg)) == ERROR) { - usage4 (_("no-swap result must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); + if ((conf_wrapper.config.no_swap_state = mp_translate_state(optarg)) == ERROR) { + usage4(_("no-swap result must be a valid state name (OK, " + "WARNING, CRITICAL, UNKNOWN) or integer (0-3).")); } break; - case 'v': /* verbose */ + case 'v': /* verbose */ verbose++; break; - case 'V': /* version */ - print_revision (progname, NP_VERSION); - exit (STATE_UNKNOWN); - case 'h': /* help */ - print_help (); - exit (STATE_UNKNOWN); - case '?': /* error */ - usage5 (); + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + case 'h': /* help */ + print_help(conf_wrapper.config); + exit(STATE_UNKNOWN); + case '?': /* error */ + usage5(); } } - c = optind; - if (c == argc) - return validate_arguments (); - - return validate_arguments (); -} - - - -int -validate_arguments (void) -{ - if ((warn.is_percentage == crit.is_percentage) && (warn.value < crit.value)) { - /* This is NOT triggered if warn and crit are different units, e.g warn is percentage - * and crit is absolute. We cannot determine the condition at this point since we - * dont know the value of total swap yet + if ((conf_wrapper.config.warn.is_percentage == conf_wrapper.config.crit.is_percentage) && + (conf_wrapper.config.warn.value < conf_wrapper.config.crit.value)) { + /* This is NOT triggered if warn and crit are different units, e.g warn + * is percentage and crit is absolute. We cannot determine the condition + * at this point since we dont know the value of total swap yet */ usage4(_("Warning should be more than critical")); } - return OK; + + return conf_wrapper; } +void print_help(swap_config config) { + print_revision(progname, NP_VERSION); + printf(_(COPYRIGHT), copyright, email); -void -print_help (void) -{ - print_revision (progname, NP_VERSION); + printf("%s\n", _("Check swap space on local machine.")); - printf (_(COPYRIGHT), copyright, email); + printf("\n\n"); - printf ("%s\n", _("Check swap space on local machine.")); + print_usage(); - printf ("\n\n"); + printf(UT_HELP_VRSN); + printf(UT_EXTRA_OPTS); - print_usage (); + printf(" %s\n", "-w, --warning=INTEGER"); + printf(" %s\n", _("Exit with WARNING status if less than INTEGER bytes " + "of swap space are free")); + printf(" %s\n", "-w, --warning=PERCENT%"); + printf(" %s\n", _("Exit with WARNING status if less than PERCENT of " + "swap space is free")); + printf(" %s\n", "-c, --critical=INTEGER"); + printf(" %s\n", _("Exit with CRITICAL status if less than INTEGER bytes " + "of swap space are free")); + printf(" %s\n", "-c, --critical=PERCENT%"); + printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of " + "swap space is free")); + printf(" %s\n", "-a, --allswaps"); + printf(" %s\n", _("Conduct comparisons for all swap partitions, one by one")); + printf(" %s\n", "-n, --no-swap="); + printf(" %s %s\n", + _("Resulting state when there is no swap regardless of thresholds. " + "Default:"), + state_text(config.no_swap_state)); + printf(UT_VERBOSE); - printf (UT_HELP_VRSN); - printf (UT_EXTRA_OPTS); + printf("\n"); + printf("%s\n", _("Notes:")); + printf(" %s\n", _("Both INTEGER and PERCENT thresholds can be specified, " + "they are all checked.")); + printf(" %s\n", _("On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s.")); - printf (" %s\n", "-w, --warning=INTEGER"); - printf (" %s\n", _("Exit with WARNING status if less than INTEGER bytes of swap space are free")); - printf (" %s\n", "-w, --warning=PERCENT%"); - printf (" %s\n", _("Exit with WARNING status if less than PERCENT of swap space is free")); - printf (" %s\n", "-c, --critical=INTEGER"); - printf (" %s\n", _("Exit with CRITICAL status if less than INTEGER bytes of swap space are free")); - printf (" %s\n", "-c, --critical=PERCENT%"); - printf (" %s\n", _("Exit with CRITICAL status if less than PERCENT of swap space is free")); - printf (" %s\n", "-a, --allswaps"); - printf (" %s\n", _("Conduct comparisons for all swap partitions, one by one")); - printf (" %s\n", "-n, --no-swap="); - printf (" %s %s\n", _("Resulting state when there is no swap regardless of thresholds. Default:"), state_text(no_swap_state)); - printf (UT_VERBOSE); - - printf ("\n"); - printf ("%s\n", _("Notes:")); - printf (" %s\n", _("Both INTEGER and PERCENT thresholds can be specified, they are all checked.")); - printf (" %s\n", _("Without thresholds, the plugin shows free swap space and performance data, but always returns OK.")); - printf (" %s\n", _("On AIX, if -a is specified, uses lsps -a, otherwise uses lsps -s.")); - - printf (UT_SUPPORT); + printf(UT_SUPPORT); } - -void -print_usage (void) -{ - printf ("%s\n", _("Usage:")); - printf (" %s [-av] [-w %%] [-c %%]\n",progname); - printf (" [-w ] [-c ] [-n ]\n"); +void print_usage(void) { + printf("%s\n", _("Usage:")); + printf(" %s [-av] -w %% -c %%\n", progname); + printf(" -w -c [-n ]\n"); } diff --git a/plugins/check_swap.d/check_swap.h b/plugins/check_swap.d/check_swap.h new file mode 100644 index 00000000..99039b21 --- /dev/null +++ b/plugins/check_swap.d/check_swap.h @@ -0,0 +1,43 @@ +#pragma once + +#include "../common.h" + +#ifndef SWAP_CONVERSION +# define SWAP_CONVERSION 1 +#endif + +typedef struct { + bool is_percentage; + uint64_t value; +} check_swap_threshold; + +typedef struct { + unsigned long long free; // Free swap in Bytes! + unsigned long long used; // Used swap in Bytes! + unsigned long long total; // Total swap size, you guessed it, in Bytes! +} swap_metrics; + +typedef struct { + int errorcode; + int statusCode; + swap_metrics metrics; +} swap_result; + +typedef struct { + bool allswaps; + int no_swap_state; + bool warn_is_set; + check_swap_threshold warn; + bool crit_is_set; + check_swap_threshold crit; + bool on_aix; + int conversion_factor; +} swap_config; + +swap_config swap_config_init(void); + +swap_result get_swap_data(swap_config config); +swap_result getSwapFromProcMeminfo(char path_to_proc_meminfo[]); +swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]); +swap_result getSwapFromSwapctl_BSD(swap_config config); +swap_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..2fe4544f --- /dev/null +++ b/plugins/check_swap.d/swap.c @@ -0,0 +1,469 @@ +#include "./check_swap.d/check_swap.h" +#include "../popen.h" +#include "../utils.h" +#include "common.h" + +extern int verbose; + +swap_config swap_config_init(void) { + swap_config tmp = {0}; + tmp.allswaps = false; + tmp.no_swap_state = STATE_CRITICAL; + tmp.conversion_factor = SWAP_CONVERSION; + + tmp.warn_is_set = false; + tmp.crit_is_set = false; + +#ifdef _AIX + tmp.on_aix = true; +#else + tmp.on_aix = false; +#endif + + return tmp; +} + +swap_result get_swap_data(swap_config config) { +#ifdef HAVE_PROC_MEMINFO + if (verbose >= 3) { + printf("Reading PROC_MEMINFO at %s\n", PROC_MEMINFO); + } + + return getSwapFromProcMeminfo(PROC_MEMINFO); +#else // HAVE_PROC_MEMINFO +# ifdef HAVE_SWAP + if (verbose >= 3) { + printf("Using swap command %s with format: %s\n", SWAP_COMMAND, SWAP_FORMAT); + } + + /* These override the command used if a summary (and thus ! allswaps) is + * required + * The summary flag returns more accurate information about swap usage on these + * OSes */ + if (config.on_aix && !config.allswaps) { + + config.conversion_factor = 1; + + return getSwapFromSwapCommand(config, "/usr/sbin/lsps -s", "%lu%*s %lu"); + } else { + return getSwapFromSwapCommand(config, SWAP_COMMAND, SWAP_FORMAT); + } +# else // HAVE_SWAP +# ifdef CHECK_SWAP_SWAPCTL_SVR4 + return getSwapFromSwapctl_SRV4(); +# else // CHECK_SWAP_SWAPCTL_SVR4 +# ifdef CHECK_SWAP_SWAPCTL_BSD + return getSwapFromSwapctl_BSD(); +# else // CHECK_SWAP_SWAPCTL_BSD +# error No way found to retrieve swap +# endif /* CHECK_SWAP_SWAPCTL_BSD */ +# endif /* CHECK_SWAP_SWAPCTL_SVR4 */ +# endif /* HAVE_SWAP */ +#endif /* HAVE_PROC_MEMINFO */ +} + +swap_result getSwapFromProcMeminfo(char proc_meminfo[]) { + FILE *meminfo_file_ptr; + meminfo_file_ptr = fopen(proc_meminfo, "r"); + + swap_result result = {0}; + result.errorcode = STATE_UNKNOWN; + + if (meminfo_file_ptr == NULL) { + // failed to open meminfo file + // errno should contain an error + result.errorcode = STATE_UNKNOWN; + return result; + } + + uint64_t swap_total = 0; + uint64_t swap_used = 0; + uint64_t swap_free = 0; + + bool found_total = false; + bool found_used = false; + bool found_free = false; + + char input_buffer[MAX_INPUT_BUFFER]; + char str[32]; + + while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, meminfo_file_ptr)) { + uint64_t tmp_KB = 0; + + /* + * The following sscanf call looks for a line looking like: "Swap: 123 + * 123 123" On which kind of system this format exists, I can not say, + * but I wanted to document this for people who are not adapt with + * sscanf anymore, like me + * Also the units used here are unclear and probably wrong + */ + if (sscanf(input_buffer, "%*[S]%*[w]%*[a]%*[p]%*[:] %lu %lu %lu", &swap_total, &swap_used, &swap_free) == 3) { + + result.metrics.total += swap_total; + result.metrics.used += swap_used; + result.metrics.free += swap_free; + + found_total = true; + found_free = true; + found_used = true; + + // Set error + result.errorcode = STATE_OK; + + /* + * The following sscanf call looks for lines looking like: + * "SwapTotal: 123" and "SwapFree: 123" This format exists at least + * on Debian Linux with a 5.* kernel + */ + } else { + int sscanf_result = sscanf(input_buffer, + "%*[S]%*[w]%*[a]%*[p]%[TotalFreCchd]%*[:] %lu " + "%*[k]%*[B]", + str, &tmp_KB); + + if (sscanf_result == 2) { + + if (verbose >= 3) { + printf("Got %s with %lu\n", str, tmp_KB); + } + + /* I think this part is always in Kb, so convert to bytes */ + if (strcmp("Total", str) == 0) { + swap_total = tmp_KB * 1000; + found_total = true; + } else if (strcmp("Free", str) == 0) { + swap_free = swap_free + tmp_KB * 1000; + found_free = true; + found_used = true; // No explicit used metric available + } else if (strcmp("Cached", str) == 0) { + swap_free = swap_free + tmp_KB * 1000; + found_free = true; + found_used = true; // No explicit used metric available + } + + result.errorcode = STATE_OK; + } + } + } + + fclose(meminfo_file_ptr); + + result.metrics.total = swap_total; + result.metrics.used = swap_total - swap_free; + result.metrics.free = swap_free; + + if (!found_free || !found_total || !found_used) { + result.errorcode = STATE_UNKNOWN; + } + + return result; +} + +swap_result getSwapFromSwapCommand(swap_config config, const char swap_command[], const char swap_format[]) { + swap_result result = {0}; + + char *temp_buffer; + + if (verbose >= 2) { + printf(_("Command: %s\n"), swap_command); + } + if (verbose >= 3) { + printf(_("Format: %s\n"), swap_format); + } + + child_process = spopen(swap_command); + if (child_process == NULL) { + printf(_("Could not open pipe: %s\n"), swap_command); + swap_result tmp = { + .errorcode = STATE_UNKNOWN, + }; + return tmp; + } + + child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); + if (child_stderr == NULL) { + printf(_("Could not open stderr for %s\n"), swap_command); + } + + char str[32] = {0}; + char input_buffer[MAX_INPUT_BUFFER]; + + /* read 1st line */ + fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); + if (strcmp(swap_format, "") == 0) { + temp_buffer = strtok(input_buffer, " \n"); + while (temp_buffer) { + if (strstr(temp_buffer, "blocks")) { + sprintf(str, "%s %s", str, "%lu"); + } else if (strstr(temp_buffer, "dskfree")) { + sprintf(str, "%s %s", str, "%lu"); + } else { + sprintf(str, "%s %s", str, "%*s"); + } + temp_buffer = strtok(NULL, " \n"); + } + } + + double total_swap_mb = 0; + double free_swap_mb = 0; + double used_swap_mb = 0; + double dsktotal_mb = 0; + double dskused_mb = 0; + double dskfree_mb = 0; + + /* + * If different swap command is used for summary switch, need to read format + * differently + */ + if (config.on_aix && !config.allswaps) { + fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); /* Ignore first line */ + sscanf(input_buffer, swap_format, &total_swap_mb, &used_swap_mb); + free_swap_mb = total_swap_mb * (100 - used_swap_mb) / 100; + used_swap_mb = total_swap_mb - free_swap_mb; + + if (verbose >= 3) { + printf(_("total=%.0f, used=%.0f, free=%.0f\n"), total_swap_mb, used_swap_mb, free_swap_mb); + } + } else { + while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { + sscanf(input_buffer, swap_format, &dsktotal_mb, &dskfree_mb); + + dsktotal_mb = dsktotal_mb / config.conversion_factor; + /* AIX lists percent used, so this converts to dskfree in MBs */ + + if (config.on_aix) { + dskfree_mb = dsktotal_mb * (100 - dskfree_mb) / 100; + } else { + dskfree_mb = dskfree_mb / config.conversion_factor; + } + + if (verbose >= 3) { + printf(_("total=%.0f, free=%.0f\n"), dsktotal_mb, dskfree_mb); + } + + dskused_mb = dsktotal_mb - dskfree_mb; + total_swap_mb += dsktotal_mb; + used_swap_mb += dskused_mb; + free_swap_mb += dskfree_mb; + } + } + + result.metrics.free = free_swap_mb * 1024 * 1024; + result.metrics.used = used_swap_mb * 1024 * 1024; + result.metrics.total = free_swap_mb * 1024 * 1024; + + /* If we get anything on STDERR, at least set warning */ + while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { + result.statusCode = max_state(result.statusCode, STATE_WARNING); + // TODO Set error here + } + + /* close stderr */ + (void)fclose(child_stderr); + + /* close the pipe */ + if (spclose(child_process)) { + result.statusCode = max_state(result.statusCode, STATE_WARNING); + // TODO set error here + } + + return result; +} + +#ifndef CHECK_SWAP_SWAPCTL_BSD +# define CHECK_SWAP_SWAPCTL_BSD + +// Stub functionality for BSD stuff, so the compiler always sees the following BSD code + +# define SWAP_NSWAP 0 +# define SWAP_STATS 1 + +int bsd_swapctl(int cmd, const void *arg, int misc) { + (void)cmd; + (void)arg; + (void)misc; + return 512; +} + +struct swapent { + dev_t se_dev; /* device id */ + int se_flags; /* entry flags */ + int se_nblks; /* total blocks */ + int se_inuse; /* blocks in use */ + int se_priority; /* priority */ + char se_path[PATH_MAX]; /* path to entry */ +}; + +#else +# define bsd_swapctl swapctl +#endif + +swap_result getSwapFromSwapctl_BSD(swap_config config) { + /* get the number of active swap devices */ + int nswaps = bsd_swapctl(SWAP_NSWAP, NULL, 0); + + /* initialize swap table + entries */ + struct swapent *ent = (struct swapent *)malloc(sizeof(struct swapent) * (unsigned long)nswaps); + + /* and now, tally 'em up */ + int swapctl_res = bsd_swapctl(SWAP_STATS, ent, nswaps); + if (swapctl_res < 0) { + perror(_("swapctl failed: ")); + die(STATE_UNKNOWN, _("Error in swapctl call\n")); + } + + double dsktotal_mb = 0.0; + double dskfree_mb = 0.0; + double dskused_mb = 0.0; + unsigned long long total_swap_mb = 0; + unsigned long long free_swap_mb = 0; + unsigned long long used_swap_mb = 0; + + for (int i = 0; i < nswaps; i++) { + dsktotal_mb = (float)ent[i].se_nblks / (float)config.conversion_factor; + dskused_mb = (float)ent[i].se_inuse / (float)config.conversion_factor; + dskfree_mb = (dsktotal_mb - dskused_mb); + + if (config.allswaps && dsktotal_mb > 0) { + double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb)); + + if (verbose) { + printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent); + } + } + + total_swap_mb += (unsigned long long)dsktotal_mb; + free_swap_mb += (unsigned long long)dskfree_mb; + used_swap_mb += (unsigned long long)dskused_mb; + } + + /* and clean up after ourselves */ + free(ent); + + swap_result result = {0}; + + result.statusCode = OK; + result.errorcode = OK; + + result.metrics.total = total_swap_mb * 1024 * 1024; + result.metrics.free = free_swap_mb * 1024 * 1024; + result.metrics.used = used_swap_mb * 1024 * 1024; + + return result; +} + +#ifndef CHECK_SWAP_SWAPCTL_SVR4 +int srv4_swapctl(int cmd, void *arg) { + (void)cmd; + (void)arg; + return 512; +} + +typedef struct srv4_swapent { + char *ste_path; /* name of the swap file */ + off_t ste_start; /* starting block for swapping */ + off_t ste_length; /* length of swap area */ + long ste_pages; /* number of pages for swapping */ + long ste_free; /* number of ste_pages free */ + long ste_flags; /* ST_INDEL bit set if swap file */ + /* is now being deleted */ +} swapent_t; + +typedef struct swaptbl { + int swt_n; /* number of swapents following */ + struct srv4_swapent swt_ent[]; /* array of swt_n swapents */ +} swaptbl_t; + +# define SC_LIST 2 +# define SC_GETNSWP 3 + +# ifndef MAXPATHLEN +# define MAXPATHLEN 2048 +# endif + +#else +# define srv4_swapctl swapctl +#endif + +swap_result getSwapFromSwap_SRV4(swap_config config) { + int nswaps = 0; + + /* get the number of active swap devices */ + if ((nswaps = srv4_swapctl(SC_GETNSWP, NULL)) == -1) { + die(STATE_UNKNOWN, _("Error getting swap devices\n")); + } + + if (nswaps == 0) { + die(STATE_OK, _("SWAP OK: No swap devices defined\n")); + } + + if (verbose >= 3) { + printf("Found %d swap device(s)\n", nswaps); + } + + /* initialize swap table + entries */ + swaptbl_t *tbl = (swaptbl_t *)malloc(sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps)); + + if (tbl == NULL) { + die(STATE_UNKNOWN, _("malloc() failed!\n")); + } + + memset(tbl, 0, sizeof(swaptbl_t) + (sizeof(swapent_t) * (unsigned long)nswaps)); + tbl->swt_n = nswaps; + + for (int i = 0; i < nswaps; i++) { + if ((tbl->swt_ent[i].ste_path = (char *)malloc(sizeof(char) * MAXPATHLEN)) == NULL) { + die(STATE_UNKNOWN, _("malloc() failed!\n")); + } + } + + /* and now, tally 'em up */ + int swapctl_res = srv4_swapctl(SC_LIST, tbl); + if (swapctl_res < 0) { + perror(_("swapctl failed: ")); + die(STATE_UNKNOWN, _("Error in swapctl call\n")); + } + + double dsktotal_mb = 0.0; + double dskfree_mb = 0.0; + double dskused_mb = 0.0; + unsigned long long total_swap_mb = 0; + unsigned long long free_swap_mb = 0; + unsigned long long used_swap_mb = 0; + + for (int i = 0; i < nswaps; i++) { + dsktotal_mb = (float)tbl->swt_ent[i].ste_pages / SWAP_CONVERSION; + dskfree_mb = (float)tbl->swt_ent[i].ste_free / SWAP_CONVERSION; + dskused_mb = (dsktotal_mb - dskfree_mb); + + if (verbose >= 3) { + printf("dsktotal_mb=%.0f dskfree_mb=%.0f dskused_mb=%.0f\n", dsktotal_mb, dskfree_mb, dskused_mb); + } + + if (config.allswaps && dsktotal_mb > 0) { + double percent = 100 * (((double)dskused_mb) / ((double)dsktotal_mb)); + + if (verbose) { + printf("[%.0f (%g%%)]", dskfree_mb, 100 - percent); + } + } + + total_swap_mb += (unsigned long long)dsktotal_mb; + free_swap_mb += (unsigned long long)dskfree_mb; + used_swap_mb += (unsigned long long)dskused_mb; + } + + /* and clean up after ourselves */ + for (int i = 0; i < nswaps; i++) { + free(tbl->swt_ent[i].ste_path); + } + free(tbl); + + swap_result result = {0}; + result.errorcode = OK; + result.metrics.total = total_swap_mb * 1024 * 1024; + result.metrics.free = free_swap_mb * 1024 * 1024; + result.metrics.used = used_swap_mb * 1024 * 1024; + + return result; +} diff --git a/plugins/t/check_swap.t b/plugins/t/check_swap.t index 18780386..eaa81083 100644 --- a/plugins/t/check_swap.t +++ b/plugins/t/check_swap.t @@ -8,9 +8,9 @@ use strict; use Test::More tests => 14; use NPTest; -my $successOutput = '/^SWAP OK - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/'; -my $failureOutput = '/^SWAP CRITICAL - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/'; -my $warnOutput = '/^SWAP WARNING - [0-9]+\% free \([0-9]+MB out of [0-9]+MB\)/'; +my $successOutput = '/^SWAP OK - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/'; +my $failureOutput = '/^SWAP CRITICAL - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/'; +my $warnOutput = '/^SWAP WARNING - [0-9]+\% free \([0-9]+MiB out of [0-9]+MiB\)/'; my $result; diff --git a/plugins/tests/test_check_swap.c b/plugins/tests/test_check_swap.c new file mode 100644 index 00000000..b85fb4ad --- /dev/null +++ b/plugins/tests/test_check_swap.c @@ -0,0 +1,23 @@ + +#include "../check_swap.d/check_swap.h" +#include "../../tap/tap.h" + +int verbose = 0; + +void print_usage(void) {} +void print_help(swap_config config) { + (void) config; +} + +const char *progname = "test_check_swap"; + +int main(void) { + swap_result test_data = getSwapFromProcMeminfo("./var/proc_meminfo"); + + plan_tests(4); + + ok(test_data.errorcode == 0, "Test whether we manage to retrieve swap data"); + ok(test_data.metrics.total == 34233905152, "Is the total Swap correct"); + ok(test_data.metrics.free == 34233905152, "Is the free Swap correct"); + ok(test_data.metrics.used == 0, "Is the used Swap correct"); +} diff --git a/plugins/tests/test_check_swap.t b/plugins/tests/test_check_swap.t new file mode 100755 index 00000000..826fae01 --- /dev/null +++ b/plugins/tests/test_check_swap.t @@ -0,0 +1,6 @@ +#!/usr/bin/perl +use Test::More; +if (! -e "./test_check_swap") { + plan skip_all => "./test_check_swap not compiled - please enable libtap library to test"; +} +exec "./test_check_swap"; diff --git a/plugins/tests/var/proc_meminfo b/plugins/tests/var/proc_meminfo new file mode 100644 index 00000000..6c5a618d --- /dev/null +++ b/plugins/tests/var/proc_meminfo @@ -0,0 +1,55 @@ +MemTotal: 32767776 kB +MemFree: 1693508 kB +MemAvailable: 23807480 kB +Buffers: 438456 kB +Cached: 19124976 kB +SwapCached: 0 kB +Active: 7860680 kB +Inactive: 18886776 kB +Active(anon): 6108756 kB +Inactive(anon): 1364500 kB +Active(file): 1751924 kB +Inactive(file): 17522276 kB +Unevictable: 8548 kB +Mlocked: 8548 kB +SwapTotal: 33431548 kB +SwapFree: 33431548 kB +Zswap: 0 kB +Zswapped: 0 kB +Dirty: 784 kB +Writeback: 0 kB +AnonPages: 7139968 kB +Mapped: 1094916 kB +Shmem: 284160 kB +KReclaimable: 3303788 kB +Slab: 3801908 kB +SReclaimable: 3303788 kB +SUnreclaim: 498120 kB +KernelStack: 32992 kB +PageTables: 68160 kB +SecPageTables: 0 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 49815436 kB +Committed_AS: 16888536 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 91200 kB +VmallocChunk: 0 kB +Percpu: 41472 kB +HardwareCorrupted: 0 kB +AnonHugePages: 1708032 kB +ShmemHugePages: 0 kB +ShmemPmdMapped: 0 kB +FileHugePages: 0 kB +FilePmdMapped: 0 kB +Unaccepted: 0 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +Hugetlb: 0 kB +DirectMap4k: 860468 kB +DirectMap2M: 20023296 kB +DirectMap1G: 12582912 kB