From aa137f7d4adb8ebbbdd3a4c71cd28824d6fcfaca Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 10 Mar 2025 22:56:34 +0100 Subject: check_curl: clang-format --- plugins/check_curl.c | 429 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 271 insertions(+), 158 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 748201e8..cf755316 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -32,8 +32,8 @@ * * *****************************************************************************/ -const char *progname = "check_curl"; +const char *progname = "check_curl"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; @@ -63,7 +63,7 @@ const char *email = "devel@monitoring-plugins.org"; #include -#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch)) +#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) #define DEFAULT_BUFFER_SIZE 2048 #define DEFAULT_SERVER_URL "/" @@ -275,12 +275,14 @@ int main(int argc, char **argv) { snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version()); /* parse arguments */ - if (process_arguments(argc, argv) == false) + if (process_arguments(argc, argv) == false) { usage4(_("Could not parse arguments")); + } - if (display_html) + if (display_html) { printf("", use_ssl ? "https" : "http", host_name ? host_name : server_address, virtual_port ? virtual_port : server_port, server_url); + } result = check_http(); return result; @@ -361,14 +363,16 @@ static int expected_statuscode(const char *reply, const char *statuscodes) { char *code; int result = 0; - if ((expected = strdup(statuscodes)) == NULL) + if ((expected = strdup(statuscodes)) == NULL) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } - for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) + for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { result = 1; break; } + } free(expected); return result; @@ -396,8 +400,9 @@ int lookup_host(const char *host, char *buf, size_t buflen) { hints.ai_flags |= AI_CANONNAME; errcode = getaddrinfo(host, NULL, &hints, &result); - if (errcode != 0) + if (errcode != 0) { return errcode; + } strcpy(buf, ""); res = result; @@ -437,23 +442,29 @@ int lookup_host(const char *host, char *buf, size_t buflen) { } static void cleanup(void) { - if (status_line_initialized) + if (status_line_initialized) { curlhelp_free_statusline(&status_line); + } status_line_initialized = false; - if (curl_easy_initialized) + if (curl_easy_initialized) { curl_easy_cleanup(curl); + } curl_easy_initialized = false; - if (curl_global_initialized) + if (curl_global_initialized) { curl_global_cleanup(); + } curl_global_initialized = false; - if (body_buf_initialized) + if (body_buf_initialized) { curlhelp_freewritebuffer(&body_buf); + } body_buf_initialized = false; - if (header_buf_initialized) + if (header_buf_initialized) { curlhelp_freewritebuffer(&header_buf); + } header_buf_initialized = false; - if (put_buf_initialized) + if (put_buf_initialized) { curlhelp_freereadbuffer(&put_buf); + } put_buf_initialized = false; } @@ -468,8 +479,9 @@ int check_http(void) { char dnscache[DEFAULT_BUFFER_SIZE]; /* initialize curl */ - if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); + } curl_global_initialized = true; if ((curl = curl_easy_init()) == NULL) { @@ -480,8 +492,9 @@ int check_http(void) { /* register cleanup function to shut down libcurl properly */ atexit(cleanup); - if (verbose >= 1) + if (verbose >= 1) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); + } /* print everything on stdout like check_http would do */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); @@ -494,16 +507,18 @@ int check_http(void) { #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&body_buf) < 0) + if (curlhelp_initwritebuffer(&body_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); + } body_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&header_buf) < 0) + if (curlhelp_initwritebuffer(&header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); + } header_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); @@ -532,8 +547,9 @@ int check_http(void) { snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr); host = curl_slist_append(NULL, dnscache); curl_easy_setopt(curl, CURLOPT_RESOLVE, host); - if (verbose >= 1) + if (verbose >= 1) { printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); + } } // If server_address is an IPv6 address it must be surround by square brackets @@ -552,16 +568,18 @@ int check_http(void) { snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http", (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url); - if (verbose >= 1) + if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); + } handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT"); - if (verbose >= 2) + if (verbose >= 2) { printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port); + } http_method = "GET"; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL"); } @@ -576,12 +594,13 @@ int check_http(void) { /* set HTTP method */ if (http_method) { - if (!strcmp(http_method, "POST")) + if (!strcmp(http_method, "POST")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); - else if (!strcmp(http_method, "PUT")) + } else if (!strcmp(http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); - else + } else { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST"); + } } /* check if Host header is explicitly set in options */ @@ -629,10 +648,12 @@ int check_http(void) { } /* client certificate and key to present to server (SSL) */ - if (client_cert) + if (client_cert) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT"); - if (client_privkey) + } + if (client_privkey) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY"); + } if (ca_cert) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO"); } @@ -700,18 +721,20 @@ int check_http(void) { } # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ - if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) + if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { add_sslctx_verify_fun = true; - else + } else { die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " "too old and has no CURLOPT_CERTINFO)\n"); + } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) + if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); + } # endif #endif /* LIBCURL_FEATURE_SSL */ @@ -720,12 +743,14 @@ int check_http(void) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); /* proxy-authentication */ - if (strcmp(proxy_auth, "")) + if (strcmp(proxy_auth, "")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD"); + } /* authentication */ - if (strcmp(user_auth, "")) + if (strcmp(user_auth, "")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD"); + } /* TODO: parameter auth method, bitfield of following methods: * CURLAUTH_BASIC (default) @@ -774,20 +799,23 @@ int check_http(void) { } /* no-body */ - if (no_body) + if (no_body) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); + } /* IPv4 or IPv6 forced DNS resolution */ - if (address_family == AF_UNSPEC) + if (address_family == AF_UNSPEC) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); - else if (address_family == AF_INET) + } else if (address_family == AF_INET) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); + } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - else if (address_family == AF_INET6) + else if (address_family == AF_INET6) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); + } #endif /* either send http POST data (any data, not only POST)*/ @@ -799,16 +827,18 @@ int check_http(void) { } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!http_post_data) + if (!http_post_data) { http_post_data = ""; + } if (!strcmp(http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); } else if (!strcmp(http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) + if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); + } put_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), @@ -821,15 +851,17 @@ int check_http(void) { /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */ - if (*cookie_jar_file) + if (*cookie_jar_file) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR"); + } } /* do the request */ res = curl_easy_perform(curl); - if (verbose >= 2 && http_post_data) + if (verbose >= 2 && http_post_data) { printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); + } /* free header and server IP resolve lists, we don't need it anymore */ curl_slist_free_all(header_list); @@ -879,8 +911,9 @@ int check_http(void) { const char *raw_cert = NULL; for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { - if (verbose >= 2) + if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); + } if (strncmp(slist->data, "Cert:", 5) == 0) { raw_cert = &slist->data[5]; goto GOT_FIRST_CERT; @@ -944,8 +977,9 @@ int check_http(void) { } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) + if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); + } /* get status line of answer, check sanity of HTTP code */ if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) { @@ -957,8 +991,9 @@ int check_http(void) { /* get result code from cURL */ handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); - if (verbose >= 2) + if (verbose >= 2) { printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); + } /* print status line, header, body if verbose */ if (verbose >= 2) { @@ -967,18 +1002,20 @@ int check_http(void) { /* make sure the status line matches the response we are looking for */ if (!expected_statuscode(status_line.first_line, server_expect)) { - if (server_port == HTTP_PORT) + if (server_port == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); - else + } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, status_line.first_line); + } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); } if (server_expect_yn) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect); - if (verbose) + if (verbose) { printf("%s\n", msg); + } result = STATE_OK; } else { /* illegal return codes result in a critical state */ @@ -1017,8 +1054,9 @@ int check_http(void) { /* libcurl redirection internally, handle error states here */ if (followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); - if (verbose >= 2) + if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + } if (redir_depth > max_depth) { snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth); die(STATE_WARNING, "HTTP WARNING - %s", msg); @@ -1130,10 +1168,11 @@ int check_http(void) { /* Cut-off trailing characters */ if (strlen(msg) >= 2) { - if (msg[strlen(msg) - 2] == ',') + if (msg[strlen(msg) - 2] == ',') { msg[strlen(msg) - 2] = '\0'; - else + } else { msg[strlen(msg) - 3] = '\0'; + } } /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ @@ -1146,16 +1185,19 @@ int check_http(void) { } int uri_strcmp(const UriTextRangeA range, const char *s) { - if (!range.first) + if (!range.first) { return -1; - if ((size_t)(range.afterLast - range.first) < strlen(s)) + } + if ((size_t)(range.afterLast - range.first) < strlen(s)) { return -1; + } return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); } char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { - if (!range.first) + if (!range.first) { return "(null)"; + } strncpy(buf, range.first, max(buflen - 1, (size_t)(range.afterLast - range.first))); buf[max(buflen - 1, (size_t)(range.afterLast - range.first))] = '\0'; buf[range.afterLast - range.first] = '\0'; @@ -1183,12 +1225,14 @@ void redir(curlhelp_write_curlbuf *header_buf) { location = get_header_value(headers, nof_headers, "location"); - if (verbose >= 2) + if (verbose >= 2) { printf(_("* Seen redirect location %s\n"), location); + } - if (++redir_depth > max_depth) + if (++redir_depth > max_depth) { die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, (display_html ? "" : "")); + } UriParserStateA state; UriUriA uri; @@ -1230,10 +1274,11 @@ void redir(curlhelp_write_curlbuf *header_buf) { } if (uri.scheme.first) { - if (!uri_strcmp(uri.scheme, "https")) + if (!uri_strcmp(uri.scheme, "https")) { use_ssl = true; - else + } else { use_ssl = false; + } } /* we do a sloppy test here only, because uriparser would have failed @@ -1243,11 +1288,13 @@ void redir(curlhelp_write_curlbuf *header_buf) { new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); } else { new_port = HTTP_PORT; - if (use_ssl) + if (use_ssl) { new_port = HTTPS_PORT; + } } - if (new_port > MAX_PORT) + if (new_port > MAX_PORT) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "" : ""); + } /* by RFC 7231 relative URLs in Location should be taken relative to * the original URL, so we try to form a new absolute URL here @@ -1255,8 +1302,9 @@ void redir(curlhelp_write_curlbuf *header_buf) { if (!uri.scheme.first && !uri.hostText.first) { new_host = strdup(host_name ? host_name : server_address); new_port = server_port; - if (use_ssl) + if (use_ssl) { uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); + } } else { new_host = strdup(uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); } @@ -1273,9 +1321,10 @@ void redir(curlhelp_write_curlbuf *header_buf) { } if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) + (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) { die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "" : "")); + } /* set new values for redirected request */ @@ -1299,9 +1348,10 @@ void redir(curlhelp_write_curlbuf *header_buf) { uriFreeUriMembersA(&uri); - if (verbose) + if (verbose) { printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); + } /* TODO: the hash component MUST be taken from the original URL and * attached to the URL in Location @@ -1313,8 +1363,9 @@ void redir(curlhelp_write_curlbuf *header_buf) { /* check whether a file exists */ void test_file(char *path) { - if (access(path, R_OK) == 0) + if (access(path, R_OK) == 0) { return; + } usage2(_("file does not exist or is not readable"), path); } @@ -1383,29 +1434,36 @@ bool process_arguments(int argc, char **argv) { {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, {0, 0, 0, 0}}; - if (argc < 2) + if (argc < 2) { return false; + } /* support check_http compatible arguments */ for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) + if (strcmp("-to", argv[c]) == 0) { strcpy(argv[c], "-t"); - if (strcmp("-hn", argv[c]) == 0) + } + if (strcmp("-hn", argv[c]) == 0) { strcpy(argv[c], "-H"); - if (strcmp("-wt", argv[c]) == 0) + } + if (strcmp("-wt", argv[c]) == 0) { strcpy(argv[c], "-w"); - if (strcmp("-ct", argv[c]) == 0) + } + if (strcmp("-ct", argv[c]) == 0) { strcpy(argv[c], "-c"); - if (strcmp("-nohtml", argv[c]) == 0) + } + if (strcmp("-nohtml", argv[c]) == 0) { strcpy(argv[c], "-n"); + } } server_url = strdup(DEFAULT_SERVER_URL); while (1) { c = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); - if (c == -1 || c == EOF || c == 1) + if (c == -1 || c == EOF || c == 1) { break; + } switch (c) { case 'h': @@ -1421,10 +1479,11 @@ bool process_arguments(int argc, char **argv) { verbose++; break; case 't': /* timeout period */ - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); - else + } else { socket_timeout = (int)strtol(optarg, NULL, 10); + } break; case 'c': /* critical time threshold */ critical_thresholds = optarg; @@ -1457,11 +1516,12 @@ bool process_arguments(int argc, char **argv) { server_url = strdup(optarg); break; case 'p': /* Server port */ - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid port number, expecting a non-negative number"), optarg); - else { - if (strtol(optarg, NULL, 10) > MAX_PORT) + } else { + if (strtol(optarg, NULL, 10) > MAX_PORT) { usage2(_("Invalid port number, supplied port number is too big"), optarg); + } server_port = (unsigned short)strtol(optarg, NULL, 10); specify_port = true; } @@ -1475,14 +1535,17 @@ bool process_arguments(int argc, char **argv) { proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ - if (!http_post_data) + if (!http_post_data) { http_post_data = strdup(optarg); - if (!http_method) + } + if (!http_method) { http_method = strdup("POST"); + } break; case 'j': /* Set HTTP method */ - if (http_method) + if (http_method) { free(http_method); + } http_method = strdup(optarg); break; case 'A': /* useragent */ @@ -1490,10 +1553,11 @@ bool process_arguments(int argc, char **argv) { user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; break; case 'k': /* Additional headers */ - if (http_opt_headers_count == 0) + if (http_opt_headers_count == 0) { http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); - else + } else { http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); + } http_opt_headers[http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ @@ -1506,18 +1570,21 @@ bool process_arguments(int argc, char **argv) { #ifdef LIBCURL_FEATURE_SSL if ((temp = strchr(optarg, ',')) != NULL) { *temp = '\0'; - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); + } days_till_exp_warn = atoi(optarg); *temp = ','; temp++; - if (!is_intnonneg(temp)) + if (!is_intnonneg(temp)) { usage2(_("Invalid certificate expiration period"), temp); + } days_till_exp_crit = atoi(temp); } else { days_till_exp_crit = 0; - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); + } days_till_exp_warn = atoi(optarg); } check_cert = true; @@ -1566,11 +1633,11 @@ bool process_arguments(int argc, char **argv) { *plus_ptr = '\0'; } - if (optarg[0] == '2') + if (optarg[0] == '2') { ssl_version = CURL_SSLVERSION_SSLv2; - else if (optarg[0] == '3') + } else if (optarg[0] == '3') { ssl_version = CURL_SSLVERSION_SSLv3; - else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) + } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) ssl_version = CURL_SSLVERSION_TLSv1_0; # else @@ -1594,8 +1661,9 @@ bool process_arguments(int argc, char **argv) { # else ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ - else + else { usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)")); + } } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) if (got_plus) { @@ -1626,10 +1694,12 @@ bool process_arguments(int argc, char **argv) { } } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ - if (verbose >= 2) + if (verbose >= 2) { printf(_("* Set SSL/TLS version to %d\n"), ssl_version); - if (!specify_port) + } + if (!specify_port) { server_port = HTTPS_PORT; + } break; #else /* LIBCURL_FEATURE_SSL */ /* -C -J and -K fall through to here without SSL */ @@ -1640,35 +1710,37 @@ bool process_arguments(int argc, char **argv) { break; #endif /* LIBCURL_FEATURE_SSL */ case MAX_REDIRS_OPTION: - if (!is_intnonneg(optarg)) + if (!is_intnonneg(optarg)) { usage2(_("Invalid max_redirs count"), optarg); - else { + } else { max_depth = atoi(optarg); } break; case 'f': /* onredirect */ - if (!strcmp(optarg, "ok")) + if (!strcmp(optarg, "ok")) { onredirect = STATE_OK; - else if (!strcmp(optarg, "warning")) + } else if (!strcmp(optarg, "warning")) { onredirect = STATE_WARNING; - else if (!strcmp(optarg, "critical")) + } else if (!strcmp(optarg, "critical")) { onredirect = STATE_CRITICAL; - else if (!strcmp(optarg, "unknown")) + } else if (!strcmp(optarg, "unknown")) { onredirect = STATE_UNKNOWN; - else if (!strcmp(optarg, "follow")) + } else if (!strcmp(optarg, "follow")) { onredirect = STATE_DEPENDENT; - else if (!strcmp(optarg, "stickyport")) + } else if (!strcmp(optarg, "stickyport")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; - else if (!strcmp(optarg, "sticky")) + } else if (!strcmp(optarg, "sticky")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; - else if (!strcmp(optarg, "follow")) + } else if (!strcmp(optarg, "follow")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; - else if (!strcmp(optarg, "curl")) + } else if (!strcmp(optarg, "curl")) { onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; - else + } else { usage2(_("Invalid onredirect option"), optarg); - if (verbose >= 2) + } + if (verbose >= 2) { printf(_("* Following redirects set to %s\n"), state_text(onredirect)); + } break; case 'd': /* string or substring */ strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); @@ -1706,12 +1778,13 @@ bool process_arguments(int argc, char **argv) { invert_regex = true; break; case STATE_REGEX: - if (!strcasecmp(optarg, "critical")) + if (!strcasecmp(optarg, "critical")) { state_regex = STATE_CRITICAL; - else if (!strcasecmp(optarg, "warning")) + } else if (!strcasecmp(optarg, "warning")) { state_regex = STATE_WARNING; - else + } else { usage2(_("Invalid state-regex option"), optarg); + } break; case '4': address_family = AF_INET; @@ -1732,17 +1805,20 @@ bool process_arguments(int argc, char **argv) { if (tmp == NULL) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); - } else + } else { min_page_len = atoi(tmp); + } tmp = strtok(NULL, ":"); if (tmp == NULL) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); - } else + } else { max_page_len = atoi(tmp); - } else + } + } else { min_page_len = atoi(optarg); + } break; } case 'N': /* no-body */ @@ -1751,20 +1827,21 @@ bool process_arguments(int argc, char **argv) { case 'M': /* max-age */ { int L = strlen(optarg); - if (L && optarg[L - 1] == 'm') + if (L && optarg[L - 1] == 'm') { maximum_age = atoi(optarg) * 60; - else if (L && optarg[L - 1] == 'h') + } else if (L && optarg[L - 1] == 'h') { maximum_age = atoi(optarg) * 60 * 60; - else if (L && optarg[L - 1] == 'd') + } else if (L && optarg[L - 1] == 'd') { maximum_age = atoi(optarg) * 60 * 60 * 24; - else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) + } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) { maximum_age = atoi(optarg); - else { + } else { fprintf(stderr, "unparsable max-age: %s\n", optarg); exit(STATE_WARNING); } - if (verbose >= 2) + if (verbose >= 2) { printf("* Maximal age of document set to %d seconds\n", maximum_age); + } } break; case 'E': /* show extended perfdata */ show_extended_perfdata = true; @@ -1807,38 +1884,47 @@ bool process_arguments(int argc, char **argv) { c = optind; - if (server_address == NULL && c < argc) + if (server_address == NULL && c < argc) { server_address = strdup(argv[c++]); + } - if (host_name == NULL && c < argc) + if (host_name == NULL && c < argc) { host_name = strdup(argv[c++]); + } if (server_address == NULL) { - if (host_name == NULL) + if (host_name == NULL) { usage4(_("You must specify a server address or host name")); - else + } else { server_address = strdup(host_name); + } } set_thresholds(&thlds, warning_thresholds, critical_thresholds); - if (critical_thresholds && thlds->critical->end > (double)socket_timeout) + if (critical_thresholds && thlds->critical->end > (double)socket_timeout) { socket_timeout = (int)thlds->critical->end + 1; - if (verbose >= 2) + } + if (verbose >= 2) { printf("* Socket timeout set to %ld seconds\n", socket_timeout); + } - if (http_method == NULL) + if (http_method == NULL) { http_method = strdup("GET"); + } - if (client_cert && !client_privkey) + if (client_cert && !client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); + } - if (virtual_port == 0) + if (virtual_port == 0) { virtual_port = server_port; - else { - if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT)) - if (!specify_port) + } else { + if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT)) { + if (!specify_port) { server_port = virtual_port; + } + } } return true; @@ -2113,8 +2199,9 @@ int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) { buf->bufsize = DEFAULT_BUFFER_SIZE; buf->buflen = 0; buf->buf = (char *)malloc((size_t)buf->bufsize); - if (buf->buf == NULL) + if (buf->buf == NULL) { return -1; + } return 0; } @@ -2156,8 +2243,9 @@ void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) { buf->buflen = datalen; buf->buf = (char *)malloc((size_t)buf->buflen); - if (buf->buf == NULL) + if (buf->buf == NULL) { return -1; + } memcpy(buf->buf, data, datalen); buf->pos = 0; return 0; @@ -2176,11 +2264,13 @@ const char *strrstr2(const char *haystack, const char *needle) { const char *prev_pos; const char *pos; - if (haystack == NULL || needle == NULL) + if (haystack == NULL || needle == NULL) { return NULL; + } - if (haystack[0] == '\0' || needle[0] == '\0') + if (haystack[0] == '\0' || needle[0] == '\0') { return NULL; + } counter = 0; prev_pos = NULL; @@ -2189,15 +2279,17 @@ const char *strrstr2(const char *haystack, const char *needle) { for (;;) { pos = strstr(pos, needle); if (pos == NULL) { - if (counter == 0) + if (counter == 0) { return NULL; + } return prev_pos; } counter++; prev_pos = pos; pos += len; - if (*pos == '\0') + if (*pos == '\0') { return prev_pos; + } } } @@ -2217,13 +2309,15 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) } first_line_end = strstr(buf, "\r\n"); - if (first_line_end == NULL) + if (first_line_end == NULL) { return -1; + } first_line_len = (size_t)(first_line_end - buf); status_line->first_line = (char *)malloc(first_line_len + 1); - if (status_line->first_line == NULL) + if (status_line->first_line == NULL) { return -1; + } memcpy(status_line->first_line, buf, first_line_len); status_line->first_line[first_line_len] = '\0'; first_line_buf = strdup(status_line->first_line); @@ -2362,8 +2456,9 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D } else { time_t srv_data = curl_getdate(server_date, NULL); time_t doc_data = curl_getdate(document_date, NULL); - if (verbose >= 2) + if (verbose >= 2) { printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); + } if (srv_data <= 0) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -2405,10 +2500,12 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D } } - if (server_date) + if (server_date) { free(server_date); - if (document_date) + } + if (document_date) { free(document_date); + } return date_result; } @@ -2438,8 +2535,9 @@ int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_ /* TODO: should we warn if the actual and the reported body length don't match? */ } - if (content_length_s) + if (content_length_s) { free(content_length_s); + } return header_buf->buflen + body_buf->buflen; } @@ -2452,28 +2550,33 @@ curlhelp_ssl_library curlhelp_get_ssl_library(void) { curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; version_data = curl_version_info(CURLVERSION_NOW); - if (version_data == NULL) + if (version_data == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; + } ssl_version = strdup(version_data->ssl_version); - if (ssl_version == NULL) + if (ssl_version == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; + } library = strtok(ssl_version, "/"); - if (library == NULL) + if (library == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; + } - if (strcmp(library, "OpenSSL") == 0) + if (strcmp(library, "OpenSSL") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; - else if (strcmp(library, "LibreSSL") == 0) + } else if (strcmp(library, "LibreSSL") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; - else if (strcmp(library, "GnuTLS") == 0) + } else if (strcmp(library, "GnuTLS") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; - else if (strcmp(library, "NSS") == 0) + } else if (strcmp(library, "NSS") == 0) { ssl_library = CURLHELP_SSL_LIBRARY_NSS; + } - if (verbose >= 2) + if (verbose >= 2) { printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); + } free(ssl_version); @@ -2503,14 +2606,16 @@ time_t parse_cert_date(const char *s) { time_t date; char *res; - if (!s) + if (!s) { return -1; + } /* Jan 17 14:25:12 2020 GMT */ res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); /* Sep 11 12:00:00 2020 GMT */ - if (res == NULL) + if (res == NULL) { strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); + } date = mktime(&tm); return date; @@ -2534,8 +2639,9 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ char timestamp[50] = ""; int status = STATE_UNKNOWN; - if (verbose >= 2) + if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); + } for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { @@ -2563,14 +2669,16 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { goto HAVE_FIRST_CERT; } - if (verbose >= 2) + if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); + } } } HAVE_FIRST_CERT: - if (verbose >= 2) + if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); + } if (!cname_found) { printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); @@ -2597,42 +2705,47 @@ HAVE_FIRST_CERT: setenv("TZ", "GMT", 1); tzset(); strftime(timestamp, 50, "%c %z", localtime(&end_date)); - if (tz) + if (tz) { setenv("TZ", tz, 1); - else + } else { unsetenv("TZ"); + } tzset(); if (days_left > 0 && days_left <= days_till_exp_warn) { printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left, timestamp); - if (days_left > days_till_exp_crit) + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else if (days_left == 0 && time_left > 0) { - if (time_left >= 3600) + if (time_left >= 3600) { time_remaining = (int)time_left / 3600; - else + } else { time_remaining = (int)time_left / 60; + } printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); - if (days_left > days_till_exp_crit) + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else if (time_left < 0) { printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); status = STATE_CRITICAL; } else if (days_left == 0) { printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp); - if (days_left > days_till_exp_crit) + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else { printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp); status = STATE_OK; -- cgit v1.2.3-74-g34f1 From f25a4000b6ffbe0fff1d749ff334b36c77ddc6a2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 11 Mar 2025 02:02:27 +0100 Subject: Refactor check_curl --- plugins/Makefile.am | 3 +- plugins/check_curl.c | 993 ++++++++++++++++++++---------------------- plugins/check_curl.d/config.h | 141 ++++++ 3 files changed, 607 insertions(+), 530 deletions(-) create mode 100644 plugins/check_curl.d/config.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 41487131..ce00c392 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -57,7 +57,8 @@ EXTRA_DIST = t \ check_apt.d \ check_by_ssh.d \ check_smtp.d \ - check_dig.d + check_dig.d \ + check_curl.d PLUGINHDRS = common.h diff --git a/plugins/check_curl.c b/plugins/check_curl.c index cf755316..4806bf14 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -37,6 +37,9 @@ const char *progname = "check_curl"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; +#include "check_curl.d/config.h" +#include "states.h" +#include "thresholds.h" #include #include @@ -65,27 +68,9 @@ const char *email = "devel@monitoring-plugins.org"; #define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) -#define DEFAULT_BUFFER_SIZE 2048 -#define DEFAULT_SERVER_URL "/" -#define HTTP_EXPECT "HTTP/" -#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN +#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN enum { MAX_IPV4_HOSTLENGTH = 255, - HTTP_PORT = 80, - HTTPS_PORT = 443, - MAX_PORT = 65535, - DEFAULT_MAX_REDIRS = 15 -}; - -enum { - STICKY_NONE = 0, - STICKY_HOST = 1, - STICKY_PORT = 2 -}; - -enum { - FOLLOW_HTTP_CURL = 0, - FOLLOW_LIBCURL = 1 }; /* for buffers for header and body */ @@ -125,46 +110,14 @@ typedef enum curlhelp_ssl_library { enum { REGS = 2, - MAX_RE_SIZE = 1024 }; + #include "regex.h" -static regex_t preg; -static regmatch_t pmatch[REGS]; -static char regexp[MAX_RE_SIZE]; -static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; -static int errcode; -static bool invert_regex = false; -static int state_regex = STATE_CRITICAL; - -static char *server_address = NULL; -static char *host_name = NULL; -static char *server_url = 0; -static struct curl_slist *server_ips = NULL; -static bool specify_port = false; -static unsigned short server_port = HTTP_PORT; -static unsigned short virtual_port = 0; -static int host_name_length; -static char output_header_search[30] = ""; -static char output_string_search[30] = ""; -static char *warning_thresholds = NULL; -static char *critical_thresholds = NULL; -static int days_till_exp_warn, days_till_exp_crit; -static thresholds *thlds; -static char user_agent[DEFAULT_BUFFER_SIZE]; -static int verbose = 0; -static bool show_extended_perfdata = false; -static bool show_body = false; -static int min_page_len = 0; -static int max_page_len = 0; -static int redir_depth = 0; -static int max_depth = DEFAULT_MAX_REDIRS; -static char *http_method = NULL; -static char *http_post_data = NULL; -static char *http_content_type = NULL; -static CURL *curl; + +// Globals static bool curl_global_initialized = false; static bool curl_easy_initialized = false; -static struct curl_slist *header_list = NULL; +static int verbose = 0; static bool body_buf_initialized = false; static curlhelp_write_curlbuf body_buf; static bool header_buf_initialized = false; @@ -173,69 +126,45 @@ static bool status_line_initialized = false; static curlhelp_statusline status_line; static bool put_buf_initialized = false; static curlhelp_read_curlbuf put_buf; -static char http_header[DEFAULT_BUFFER_SIZE]; + +static struct curl_slist *server_ips = NULL; // TODO maybe unused +static int redir_depth = 0; // Maybe global +static CURL *curl; +static struct curl_slist *header_list = NULL; static long code; -static long socket_timeout = DEFAULT_SOCKET_TIMEOUT; -static double total_time; -static double time_connect; -static double time_appconnect; -static double time_headers; -static double time_firstbyte; static char errbuf[MAX_INPUT_BUFFER]; -static CURLcode res; -static char url[DEFAULT_BUFFER_SIZE]; static char msg[DEFAULT_BUFFER_SIZE]; -static char perfstring[DEFAULT_BUFFER_SIZE]; -static char header_expect[MAX_INPUT_BUFFER] = ""; -static char string_expect[MAX_INPUT_BUFFER] = ""; -static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; -static int server_expect_yn = 0; -static char user_auth[MAX_INPUT_BUFFER] = ""; -static char proxy_auth[MAX_INPUT_BUFFER] = ""; -static char **http_opt_headers; -static int http_opt_headers_count = 0; -static bool display_html = false; -static int onredirect = STATE_OK; -static int followmethod = FOLLOW_HTTP_CURL; -static int followsticky = STICKY_NONE; -static bool use_ssl = false; -static bool check_cert = false; -static bool continue_after_check_cert = false; typedef union { struct curl_slist *to_info; struct curl_certinfo *to_certinfo; } cert_ptr_union; static cert_ptr_union cert_ptr; -static int ssl_version = CURL_SSLVERSION_DEFAULT; -static char *client_cert = NULL; -static char *client_privkey = NULL; -static char *ca_cert = NULL; -static bool verify_peer_and_host = false; static bool is_openssl_callback = false; static bool add_sslctx_verify_fun = false; + #if defined(HAVE_SSL) && defined(USE_OPENSSL) static X509 *cert = NULL; #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static bool no_body = false; -static int maximum_age = -1; + static int address_family = AF_UNSPEC; static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; -static int curl_http_version = CURL_HTTP_VERSION_NONE; -static bool automatic_decompression = false; -static char *cookie_jar_file = NULL; -static bool haproxy_protocol = false; -static bool process_arguments(int /*argc*/, char ** /*argv*/); +typedef struct { + int errorcode; + check_curl_config config; +} check_curl_config_wrapper; +static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); + static void handle_curl_option_return_code(CURLcode res, const char *option); -static int check_http(void); -static void redir(curlhelp_write_curlbuf * /*header_buf*/); -static char *perfd_time(double elapsed_time); -static char *perfd_time_connect(double elapsed_time_connect); -static char *perfd_time_ssl(double elapsed_time_ssl); -static char *perfd_time_firstbyte(double elapsed_time_firstbyte); -static char *perfd_time_headers(double elapsed_time_headers); -static char *perfd_time_transfer(double elapsed_time_transfer); -static char *perfd_size(int page_len); +static mp_state_enum check_http(check_curl_config /*config*/); +static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/); +static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); +static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); +static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); +static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); +static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); +static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); +static char *perfd_size(int page_len, int /*min_page_len*/); static void print_help(void); void print_usage(void); static void print_curl_version(void); @@ -252,7 +181,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *, int, int); static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE]); +static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) @@ -262,8 +191,6 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int static void test_file(char * /*path*/); int main(int argc, char **argv) { - int result = STATE_UNKNOWN; - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -271,21 +198,27 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - /* set defaults */ - snprintf(user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version()); - /* parse arguments */ - if (process_arguments(argc, argv) == false) { + check_curl_config_wrapper tmp_config = process_arguments(argc, argv); + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } - if (display_html) { - printf("", use_ssl ? "https" : "http", host_name ? host_name : server_address, - virtual_port ? virtual_port : server_port, server_url); + const check_curl_config config = tmp_config.config; + + /* set defaults */ + if (config.user_agent == NULL) { + snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, + curl_version()); } - result = check_http(); - return result; + if (config.display_html) { + printf("", config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, config.virtual_port ? config.virtual_port : config.server_port, + config.server_url); + } + + exit(check_http(config)); } #ifdef HAVE_SSL @@ -360,13 +293,13 @@ static char *string_statuscode(int major, int minor) { /* Checks if the server 'reply' is one of the expected 'statuscodes' */ static int expected_statuscode(const char *reply, const char *statuscodes) { char *expected; - char *code; - int result = 0; if ((expected = strdup(statuscodes)) == NULL) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); } + char *code; + int result = 0; for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { result = 1; @@ -387,26 +320,25 @@ void handle_curl_option_return_code(CURLcode res, const char *option) { } int lookup_host(const char *host, char *buf, size_t buflen) { - struct addrinfo hints, *res, *result; - char addrstr[100]; - size_t addrstr_len; - int errcode; - void *ptr = {0}; - size_t buflen_remaining = buflen - 1; - + struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_CANONNAME; - errcode = getaddrinfo(host, NULL, &hints, &result); + struct addrinfo *result; + int errcode = getaddrinfo(host, NULL, &hints, &result); if (errcode != 0) { return errcode; } strcpy(buf, ""); - res = result; + struct addrinfo *res = result; + size_t buflen_remaining = buflen - 1; + size_t addrstr_len; + char addrstr[100]; + void *ptr = {0}; while (res) { switch (res->ai_family) { case AF_INET: @@ -468,16 +400,7 @@ static void cleanup(void) { put_buf_initialized = false; } -int check_http(void) { - int result = STATE_OK; - int result_ssl = STATE_OK; - int page_len = 0; - int i; - char *force_host_header = NULL; - struct curl_slist *host = NULL; - char addrstr[DEFAULT_BUFFER_SIZE / 2]; - char dnscache[DEFAULT_BUFFER_SIZE]; - +mp_state_enum check_http(check_curl_config config) { /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); @@ -499,7 +422,7 @@ int check_http(void) { /* print everything on stdout like check_http would do */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); - if (automatic_decompression) + if (config.automatic_decompression) #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); #else @@ -528,23 +451,27 @@ int check_http(void) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER"); /* set timeouts */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ - if (haproxy_protocol) { + if (config.haproxy_protocol) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL"); } // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we // use the host_name later on to make SNI happy - if (use_ssl && host_name != NULL) { - if ((res = lookup_host(server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), server_address, res, - gai_strerror(res)); + struct curl_slist *host = NULL; + char dnscache[DEFAULT_BUFFER_SIZE]; + char addrstr[DEFAULT_BUFFER_SIZE / 2]; + if (config.use_ssl && config.host_name != NULL) { + CURLcode res; + if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + config.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, addrstr); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, addrstr); host = curl_slist_append(NULL, dnscache); curl_easy_setopt(curl, CURLOPT_RESOLVE, host); if (verbose >= 1) { @@ -554,19 +481,21 @@ int check_http(void) { // If server_address is an IPv6 address it must be surround by square brackets struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(server_address) + 3); + if (inet_pton(AF_INET6, config.server_address, &tmp_in_addr) == 1) { + char *new_server_address = malloc(strlen(config.server_address) + 3); if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(server_address) + 3, "[%s]", server_address); - free(server_address); - server_address = new_server_address; + snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", config.server_address); + free(config.server_address); + config.server_address = new_server_address; } /* compose URL: use the address we want to connect to, set Host: header later */ - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", use_ssl ? "https" : "http", - (use_ssl & (host_name != NULL)) ? host_name : server_address, server_port, server_url); + char url[DEFAULT_BUFFER_SIZE]; + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", config.use_ssl ? "https" : "http", + (config.use_ssl & (config.host_name != NULL)) ? config.host_name : config.server_address, config.server_port, + config.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); @@ -574,50 +503,52 @@ int check_http(void) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT"); + if (!strcmp(config.http_method, "CONNECT") || strstr(config.server_url, "http") == config.server_url) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), "CURLOPT_PROXY"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), "CURLOPT_PROXYPORT"); if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port); + printf("* curl CURLOPT_PROXY: %s:%d\n", config.server_address, config.server_port); } - http_method = "GET"; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, server_url), "CURLOPT_URL"); + config.http_method = "GET"; + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), "CURLOPT_URL"); } /* disable body for HEAD request */ - if (http_method && !strcmp(http_method, "HEAD")) { - no_body = true; + if (config.http_method && !strcmp(config.http_method, "HEAD")) { + config.no_body = true; } /* set HTTP protocol version */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, curl_http_version), "CURLOPT_HTTP_VERSION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); /* set HTTP method */ - if (http_method) { - if (!strcmp(http_method, "POST")) { + if (config.http_method) { + if (!strcmp(config.http_method, "POST")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); - } else if (!strcmp(http_method, "PUT")) { + } else if (!strcmp(config.http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), "CURLOPT_CUSTOMREQUEST"); } } + char *force_host_header = NULL; /* check if Host header is explicitly set in options */ - if (http_opt_headers_count) { - for (i = 0; i < http_opt_headers_count; i++) { - if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { - force_host_header = http_opt_headers[i]; + if (config.http_opt_headers_count) { + for (int i = 0; i < config.http_opt_headers_count; i++) { + if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { + force_host_header = config.http_opt_headers[i]; } } } /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */ - if (host_name != NULL && force_host_header == NULL) { - if ((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port); + char http_header[DEFAULT_BUFFER_SIZE]; + if (config.host_name != NULL && force_host_header == NULL) { + if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || (config.virtual_port != HTTPS_PORT && config.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, config.virtual_port); } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", config.host_name); } header_list = curl_slist_append(header_list, http_header); } @@ -628,9 +559,9 @@ int check_http(void) { /* attach additional headers supplied by the user */ /* optionally send any other header tag */ - if (http_opt_headers_count) { - for (i = 0; i < http_opt_headers_count; i++) { - header_list = curl_slist_append(header_list, http_opt_headers[i]); + if (config.http_opt_headers_count) { + for (int i = 0; i < config.http_opt_headers_count; i++) { + header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } /* This cannot be free'd here because a redirection will then try to access this and segfault */ /* Covered in a testcase in tests/check_http.t */ @@ -643,21 +574,21 @@ int check_http(void) { #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ - if (use_ssl) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION"); + if (config.use_ssl) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ - if (client_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT"); + if (config.client_cert) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), "CURLOPT_SSLCERT"); } - if (client_privkey) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY"); + if (config.client_privkey) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); } - if (ca_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO"); + if (config.ca_cert) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); } - if (ca_cert || verify_peer_and_host) { + if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); @@ -675,7 +606,7 @@ int check_http(void) { ssl_library = curlhelp_get_ssl_library(); /* try hard to get a stack of certificates to verify against */ - if (check_cert) { + if (config.check_cert) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) /* inform curl to report back certificates */ switch (ssl_library) { @@ -740,16 +671,16 @@ int check_http(void) { #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), "CURLOPT_USERAGENT"); /* proxy-authentication */ - if (strcmp(proxy_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD"); + if (strcmp(config.proxy_auth, "")) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); } /* authentication */ - if (strcmp(user_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD"); + if (strcmp(config.user_auth, "")) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), "CURLOPT_USERPWD"); } /* TODO: parameter auth method, bitfield of following methods: @@ -768,14 +699,14 @@ int check_http(void) { */ /* handle redirections */ - if (onredirect == STATE_DEPENDENT) { - if (followmethod == FOLLOW_LIBCURL) { + if (config.onredirect == STATE_DEPENDENT) { + if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) @@ -799,7 +730,7 @@ int check_http(void) { } /* no-body */ - if (no_body) { + if (config.no_body) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); } @@ -819,48 +750,48 @@ int check_http(void) { #endif /* either send http POST data (any data, not only POST)*/ - if (!strcmp(http_method, "POST") || !strcmp(http_method, "PUT")) { + if (!strcmp(config.http_method, "POST") || !strcmp(config.http_method, "PUT")) { /* set content of payload for POST and PUT */ - if (http_content_type) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type); + if (config.http_content_type) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", config.http_content_type); header_list = curl_slist_append(header_list, http_header); } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!http_post_data) { - http_post_data = ""; + if (!config.http_post_data) { + config.http_post_data = ""; } - if (!strcmp(http_method, "POST")) { + if (!strcmp(config.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS"); - } else if (!strcmp(http_method, "PUT")) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), "CURLOPT_POSTFIELDS"); + } else if (!strcmp(config.http_method, "PUT")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, http_post_data, strlen(http_post_data)) < 0) { + if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, strlen(config.http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } put_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(http_post_data)), + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(config.http_post_data)), "CURLOPT_INFILESIZE"); } } /* cookie handling */ - if (cookie_jar_file != NULL) { + if (config.cookie_jar_file != NULL) { /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookie_jar_file), "CURLOPT_COOKIEFILE"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */ - if (*cookie_jar_file) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookie_jar_file), "CURLOPT_COOKIEJAR"); + if (*config.cookie_jar_file) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); } } /* do the request */ - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); - if (verbose >= 2 && http_post_data) { - printf("**** REQUEST CONTENT ****\n%s\n", http_post_data); + if (verbose >= 2 && config.http_post_data) { + printf("**** REQUEST CONTENT ****\n%s\n", config.http_post_data); } /* free header and server IP resolve lists, we don't need it anymore */ @@ -875,22 +806,23 @@ int check_http(void) { /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), server_port, - res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), + config.server_port, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } + int result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL - if (use_ssl) { - if (check_cert) { + if (config.use_ssl) { + if (config.check_cert) { if (is_openssl_callback) { # ifdef USE_OPENSSL /* check certificate with OpenSSL functions, curl has been built against OpenSSL * and we actually have OpenSSL in the monitoring tools */ - result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); - if (!continue_after_check_cert) { + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ @@ -898,7 +830,6 @@ int check_http(void) { "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n"); # endif /* USE_OPENSSL */ } else { - int i; struct curl_slist *slist; cert_ptr.to_info = NULL; @@ -909,7 +840,7 @@ int check_http(void) { * We only check the first certificate and assume it's the one of the server */ const char *raw_cert = NULL; - for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { + for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); @@ -934,8 +865,8 @@ int check_http(void) { die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO_free(cert_BIO); - result_ssl = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); - if (!continue_after_check_cert) { + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ @@ -960,20 +891,30 @@ int check_http(void) { /* we got the data and we executed the request in a given time, so we can append * performance data to the answer always */ + double total_time; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - page_len = get_content_length(&header_buf, &body_buf); - if (show_extended_perfdata) { + int page_len = get_content_length(&header_buf, &body_buf); + char perfstring[DEFAULT_BUFFER_SIZE]; + if (config.show_extended_perfdata) { + double time_connect; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + double time_appconnect; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); + double time_headers; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); + double time_firstbyte; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time), perfd_size(page_len), - perfd_time_connect(time_connect), use_ssl ? perfd_time_ssl(time_appconnect - time_connect) : "", - perfd_time_headers(time_headers - time_appconnect), perfd_time_firstbyte(time_firstbyte - time_headers), - perfd_time_transfer(total_time - time_firstbyte)); + + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_size(page_len, config.min_page_len), perfd_time_connect(time_connect, config.socket_timeout), + config.use_ssl ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) : "", + perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), + perfd_time_firstbyte(time_firstbyte - time_headers, config.socket_timeout), + perfd_time_transfer(total_time - time_firstbyte, config.socket_timeout)); } else { - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time), perfd_size(page_len)); + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_size(page_len, config.min_page_len)); } /* return a CRITICAL status if we couldn't read any data */ @@ -997,22 +938,23 @@ int check_http(void) { /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (no_body ? " [[ skipped ]]" : body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (config.no_body ? " [[ skipped ]]" : body_buf.buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(status_line.first_line, server_expect)) { - if (server_port == HTTP_PORT) { + if (!expected_statuscode(status_line.first_line, config.server_expect)) { + if (config.server_port == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), config.server_port, status_line.first_line); } - die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, show_body ? "\n" : "", show_body ? body_buf.buf : ""); + die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", config.show_body ? body_buf.buf : ""); } - if (server_expect_yn) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect); + int result = STATE_OK; + if (config.server_expect_yn) { + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); if (verbose) { printf("%s\n", msg); } @@ -1029,22 +971,22 @@ int check_http(void) { result = STATE_WARNING; /* check redirected page if specified */ } else if (code >= 300) { - if (onredirect == STATE_DEPENDENT) { - if (followmethod == FOLLOW_LIBCURL) { + if (config.onredirect == STATE_DEPENDENT) { + if (config.followmethod == FOLLOW_LIBCURL) { code = status_line.http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ - redir(&header_buf); + redir(&header_buf, config); } } else { /* this is a specific code in the command line to * be returned when a redirection is encountered */ } - result = max_state_alt(onredirect, result); + result = max_state_alt(config.onredirect, result); /* all other codes are considered ok */ } else { result = STATE_OK; @@ -1052,13 +994,13 @@ int check_http(void) { } /* libcurl redirection internally, handle error states here */ - if (followmethod == FOLLOW_LIBCURL) { + if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); } - if (redir_depth > max_depth) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", max_depth); + if (redir_depth > config.max_depth) { + snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", config.max_depth); die(STATE_WARNING, "HTTP WARNING - %s", msg); } } @@ -1069,16 +1011,17 @@ int check_http(void) { string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); } - if (maximum_age >= 0) { - result = max_state_alt(check_document_dates(&header_buf, &msg), result); + if (config.maximum_age >= 0) { + result = max_state_alt(check_document_dates(&header_buf, &msg, config.maximum_age), result); } /* Page and Header content checks go here */ - if (strlen(header_expect)) { - if (!strstr(header_buf.buf, header_expect)) { + if (strlen(config.header_expect)) { + if (!strstr(header_buf.buf, config.header_expect)) { - strncpy(&output_header_search[0], header_expect, sizeof(output_header_search)); + char output_header_search[30] = ""; + strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); if (output_header_search[sizeof(output_header_search) - 1] != '\0') { bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); @@ -1087,7 +1030,8 @@ int check_http(void) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, - use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); + config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, + config.server_url); strcpy(msg, tmp); @@ -1095,10 +1039,11 @@ int check_http(void) { } } - if (strlen(string_expect)) { - if (!strstr(body_buf.buf, string_expect)) { + if (strlen(config.string_expect)) { + if (!strstr(body_buf.buf, config.string_expect)) { - strncpy(&output_string_search[0], string_expect, sizeof(output_string_search)); + char output_string_search[30] = ""; + strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); if (output_string_search[sizeof(output_string_search) - 1] != '\0') { bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); @@ -1107,7 +1052,8 @@ int check_http(void) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, - use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); + config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, + config.server_url); strcpy(msg, tmp); @@ -1115,13 +1061,15 @@ int check_http(void) { } } - if (strlen(regexp)) { - errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); - if ((errcode == 0 && !invert_regex) || (errcode == REG_NOMATCH && invert_regex)) { + if (strlen(config.regexp)) { + regex_t preg; + regmatch_t pmatch[REGS]; + int errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); + if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ result = max_state_alt(STATE_OK, result); - } else if ((errcode == REG_NOMATCH && !invert_regex) || (errcode == 0 && invert_regex)) { - if (!invert_regex) { + } else if ((errcode == REG_NOMATCH && !config.invert_regex) || (errcode == 0 && config.invert_regex)) { + if (!config.invert_regex) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); @@ -1133,7 +1081,7 @@ int check_http(void) { snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); strcpy(msg, tmp); } - result = state_regex; + result = config.state_regex; } else { regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); @@ -1146,7 +1094,7 @@ int check_http(void) { } /* make sure the page is of an appropriate size */ - if ((max_page_len > 0) && (page_len > max_page_len)) { + if ((config.max_page_len > 0) && (page_len > config.max_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); @@ -1155,7 +1103,7 @@ int check_http(void) { result = max_state_alt(STATE_WARNING, result); - } else if ((min_page_len > 0) && (page_len < min_page_len)) { + } else if ((config.min_page_len > 0) && (page_len < config.min_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); @@ -1164,7 +1112,7 @@ int check_http(void) { } /* -w, -c: check warning and critical level */ - result = max_state_alt(get_status(total_time, thlds), result); + result = max_state_alt(get_status(total_time, config.thlds), result); /* Cut-off trailing characters */ if (strlen(msg) >= 2) { @@ -1178,8 +1126,8 @@ int check_http(void) { /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ die(max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, - strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (display_html ? "" : ""), perfstring, (show_body ? body_buf.buf : ""), - (show_body ? "\n" : "")); + strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, + (config.show_body ? body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } @@ -1204,18 +1152,11 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { return buf; } -void redir(curlhelp_write_curlbuf *header_buf) { - char *location = NULL; +void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { curlhelp_statusline status_line; struct phr_header headers[255]; - size_t nof_headers = 255; size_t msglen; - char buf[DEFAULT_BUFFER_SIZE]; - char ipstr[INET_ADDR_MAX_SIZE]; - int new_port; - char *new_host; - char *new_url; - + size_t nof_headers = 255; int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); @@ -1223,15 +1164,15 @@ void redir(curlhelp_write_curlbuf *header_buf) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); } - location = get_header_value(headers, nof_headers, "location"); + char *location = get_header_value(headers, nof_headers, "location"); if (verbose >= 2) { printf(_("* Seen redirect location %s\n"), location); } - if (++redir_depth > max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), max_depth, location, - (display_html ? "" : "")); + if (++redir_depth > config.max_depth) { + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), config.max_depth, location, + (config.display_html ? "" : "")); } UriParserStateA state; @@ -1239,12 +1180,15 @@ void redir(curlhelp_write_curlbuf *header_buf) { state.uri = &uri; if (uriParseUriA(&state, location) != URI_SUCCESS) { if (state.errorCode == URI_ERROR_SYNTAX) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, (display_html ? "" : "")); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, + (config.display_html ? "" : "")); } else if (state.errorCode == URI_ERROR_MALLOC) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); } } + char ipstr[INET_ADDR_MAX_SIZE]; + char buf[DEFAULT_BUFFER_SIZE]; if (verbose >= 2) { printf(_("** scheme: %s\n"), uri_string(uri.scheme, buf, DEFAULT_BUFFER_SIZE)); printf(_("** host: %s\n"), uri_string(uri.hostText, buf, DEFAULT_BUFFER_SIZE)); @@ -1274,35 +1218,34 @@ void redir(curlhelp_write_curlbuf *header_buf) { } if (uri.scheme.first) { - if (!uri_strcmp(uri.scheme, "https")) { - use_ssl = true; - } else { - use_ssl = false; - } + config.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https")); } /* we do a sloppy test here only, because uriparser would have failed * above, if the port would be invalid, we just check for MAX_PORT */ + int new_port; if (uri.portText.first) { new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); } else { new_port = HTTP_PORT; - if (use_ssl) { + if (config.use_ssl) { new_port = HTTPS_PORT; } } if (new_port > MAX_PORT) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, display_html ? "" : ""); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, + config.display_html ? "" : ""); } /* by RFC 7231 relative URLs in Location should be taken relative to * the original URL, so we try to form a new absolute URL here */ + char *new_host; if (!uri.scheme.first && !uri.hostText.first) { - new_host = strdup(host_name ? host_name : server_address); - new_port = server_port; - if (use_ssl) { + new_host = strdup(config.host_name ? config.host_name : config.server_address); + new_port = config.server_port; + if (config.use_ssl) { uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); } } else { @@ -1311,7 +1254,7 @@ void redir(curlhelp_write_curlbuf *header_buf) { /* compose new path */ /* TODO: handle fragments and query part of URL */ - new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); + char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); if (uri.pathHead) { const UriPathSegmentA *p = uri.pathHead; for (; p; p = p->next) { @@ -1320,37 +1263,37 @@ void redir(curlhelp_write_curlbuf *header_buf) { } } - if (server_port == new_port && !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, new_url)) { - die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), use_ssl ? "https" : "http", - new_host, new_port, new_url, (display_html ? "" : "")); + if (config.server_port == new_port && !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && + (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(config.server_url, new_url)) { + die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), config.use_ssl ? "https" : "http", + new_host, new_port, new_url, (config.display_html ? "" : "")); } /* set new values for redirected request */ - if (!(followsticky & STICKY_HOST)) { - free(server_address); - server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); + if (!(config.followsticky & STICKY_HOST)) { + free(config.server_address); + config.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); } - if (!(followsticky & STICKY_PORT)) { - server_port = (unsigned short)new_port; + if (!(config.followsticky & STICKY_PORT)) { + config.server_port = (unsigned short)new_port; } - free(host_name); - host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); + free(config.host_name); + config.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); /* reset virtual port */ - virtual_port = server_port; + config.virtual_port = config.server_port; free(new_host); - free(server_url); - server_url = new_url; + free(config.server_url); + config.server_url = new_url; uriFreeUriMembersA(&uri); if (verbose) { - printf(_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, - server_url); + printf(_("Redirection to %s://%s:%d%s\n"), config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, config.server_port, config.server_url); } /* TODO: the hash component MUST be taken from the original URL and @@ -1358,7 +1301,7 @@ void redir(curlhelp_write_curlbuf *header_buf) { */ cleanup(); - check_http(); + check_http(config); } /* check whether a file exists */ @@ -1369,11 +1312,7 @@ void test_file(char *path) { usage2(_("file does not exist or is not readable"), path); } -bool process_arguments(int argc, char **argv) { - char *p; - int c = 1; - char *temp; - +check_curl_config_wrapper process_arguments(int argc, char **argv) { enum { INVERT_REGEX = CHAR_MAX + 1, SNI_OPTION, @@ -1387,8 +1326,6 @@ bool process_arguments(int argc, char **argv) { STATE_REGEX }; - int option = 0; - int got_plus = 0; static struct option longopts[] = {STD_LONG_OPTS, {"link", no_argument, 0, 'L'}, {"nohtml", no_argument, 0, 'n'}, @@ -1434,38 +1371,48 @@ bool process_arguments(int argc, char **argv) { {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, {0, 0, 0, 0}}; + check_curl_config_wrapper result = { + .errorcode = OK, + .config = check_curl_config_init(), + }; + if (argc < 2) { - return false; + result.errorcode = ERROR; + return result; } /* support check_http compatible arguments */ - for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) { - strcpy(argv[c], "-t"); + for (int index = 1; index < argc; index++) { + if (strcmp("-to", argv[index]) == 0) { + strcpy(argv[index], "-t"); } - if (strcmp("-hn", argv[c]) == 0) { - strcpy(argv[c], "-H"); + if (strcmp("-hn", argv[index]) == 0) { + strcpy(argv[index], "-H"); } - if (strcmp("-wt", argv[c]) == 0) { - strcpy(argv[c], "-w"); + if (strcmp("-wt", argv[index]) == 0) { + strcpy(argv[index], "-w"); } - if (strcmp("-ct", argv[c]) == 0) { - strcpy(argv[c], "-c"); + if (strcmp("-ct", argv[index]) == 0) { + strcpy(argv[index], "-c"); } - if (strcmp("-nohtml", argv[c]) == 0) { - strcpy(argv[c], "-n"); + if (strcmp("-nohtml", argv[index]) == 0) { + strcpy(argv[index], "-n"); } } - server_url = strdup(DEFAULT_SERVER_URL); - - while (1) { - c = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); - if (c == -1 || c == EOF || c == 1) { + int option = 0; + char *warning_thresholds = NULL; + char *critical_thresholds = NULL; + int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; + bool specify_port = false; + + while (true) { + int option_index = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); + if (option_index == -1 || option_index == EOF || option_index == 1) { break; } - switch (c) { + switch (option_index) { case 'h': print_help(); exit(STATE_UNKNOWN); @@ -1482,7 +1429,7 @@ bool process_arguments(int argc, char **argv) { if (!is_intnonneg(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); } else { - socket_timeout = (int)strtol(optarg, NULL, 10); + result.config.socket_timeout = (int)strtol(optarg, NULL, 10); } break; case 'c': /* critical time threshold */ @@ -1492,28 +1439,30 @@ bool process_arguments(int argc, char **argv) { warning_thresholds = optarg; break; case 'H': /* virtual host */ - host_name = strdup(optarg); - if (host_name[0] == '[') { - if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */ - virtual_port = atoi(p + 2); + result.config.host_name = strdup(optarg); + char *p; + int host_name_length; + if (result.config.host_name[0] == '[') { + if ((p = strstr(result.config.host_name, "]:")) != NULL) { /* [IPv6]:port */ + result.config.virtual_port = atoi(p + 2); /* cut off the port */ - host_name_length = strlen(host_name) - strlen(p) - 1; - free(host_name); - host_name = strndup(optarg, host_name_length); + host_name_length = strlen(result.config.host_name) - strlen(p) - 1; + free(result.config.host_name); + result.config.host_name = strndup(optarg, host_name_length); } - } else if ((p = strchr(host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ - virtual_port = atoi(p); + } else if ((p = strchr(result.config.host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ + result.config.virtual_port = atoi(p); /* cut off the port */ - host_name_length = strlen(host_name) - strlen(p) - 1; - free(host_name); - host_name = strndup(optarg, host_name_length); + host_name_length = strlen(result.config.host_name) - strlen(p) - 1; + free(result.config.host_name); + result.config.host_name = strndup(optarg, host_name_length); } break; case 'I': /* internet address */ - server_address = strdup(optarg); + result.config.server_address = strdup(optarg); break; case 'u': /* URL path */ - server_url = strdup(optarg); + result.config.server_url = strdup(optarg); break; case 'p': /* Server port */ if (!is_intnonneg(optarg)) { @@ -1522,144 +1471,148 @@ bool process_arguments(int argc, char **argv) { if (strtol(optarg, NULL, 10) > MAX_PORT) { usage2(_("Invalid port number, supplied port number is too big"), optarg); } - server_port = (unsigned short)strtol(optarg, NULL, 10); + result.config.server_port = (unsigned short)strtol(optarg, NULL, 10); specify_port = true; } break; case 'a': /* authorization info */ - strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1); - user_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.user_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.user_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'b': /* proxy-authorization info */ - strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1); - proxy_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ - if (!http_post_data) { - http_post_data = strdup(optarg); + if (!result.config.http_post_data) { + result.config.http_post_data = strdup(optarg); } - if (!http_method) { - http_method = strdup("POST"); + if (!result.config.http_method) { + result.config.http_method = strdup("POST"); } break; case 'j': /* Set HTTP method */ - if (http_method) { - free(http_method); + if (result.config.http_method) { + free(result.config.http_method); } - http_method = strdup(optarg); + result.config.http_method = strdup(optarg); break; case 'A': /* useragent */ - strncpy(user_agent, optarg, DEFAULT_BUFFER_SIZE); - user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; + strncpy(result.config.user_agent, optarg, DEFAULT_BUFFER_SIZE); + result.config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; break; case 'k': /* Additional headers */ - if (http_opt_headers_count == 0) { - http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); + if (result.config.http_opt_headers_count == 0) { + result.config.http_opt_headers = malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); } else { - http_opt_headers = realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); + result.config.http_opt_headers = + realloc(result.config.http_opt_headers, sizeof(char *) * (++result.config.http_opt_headers_count)); } - http_opt_headers[http_opt_headers_count - 1] = optarg; + result.config.http_opt_headers[result.config.http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ - display_html = true; + result.config.display_html = true; break; case 'n': /* do not show html link */ - display_html = false; + result.config.display_html = false; break; case 'C': /* Check SSL cert validity */ #ifdef LIBCURL_FEATURE_SSL + char *temp; if ((temp = strchr(optarg, ',')) != NULL) { *temp = '\0'; if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); } - days_till_exp_warn = atoi(optarg); + result.config.days_till_exp_warn = atoi(optarg); *temp = ','; temp++; if (!is_intnonneg(temp)) { usage2(_("Invalid certificate expiration period"), temp); } - days_till_exp_crit = atoi(temp); + result.config.days_till_exp_crit = atoi(temp); } else { - days_till_exp_crit = 0; + result.config.days_till_exp_crit = 0; if (!is_intnonneg(optarg)) { usage2(_("Invalid certificate expiration period"), optarg); } - days_till_exp_warn = atoi(optarg); + result.config.days_till_exp_warn = atoi(optarg); } - check_cert = true; + result.config.check_cert = true; goto enable_ssl; #endif case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ #ifdef HAVE_SSL - continue_after_check_cert = true; + result.config.continue_after_check_cert = true; break; #endif case 'J': /* use client certificate */ #ifdef LIBCURL_FEATURE_SSL test_file(optarg); - client_cert = optarg; + result.config.client_cert = optarg; goto enable_ssl; #endif case 'K': /* use client private key */ #ifdef LIBCURL_FEATURE_SSL test_file(optarg); - client_privkey = optarg; + result.config.client_privkey = optarg; goto enable_ssl; #endif #ifdef LIBCURL_FEATURE_SSL case CA_CERT_OPTION: /* use CA chain file */ test_file(optarg); - ca_cert = optarg; + result.config.ca_cert = optarg; goto enable_ssl; #endif #ifdef LIBCURL_FEATURE_SSL case 'D': /* verify peer certificate & host */ - verify_peer_and_host = true; + result.config.verify_peer_and_host = true; break; #endif case 'S': /* use SSL */ #ifdef LIBCURL_FEATURE_SSL + { enable_ssl: - use_ssl = true; + bool got_plus = false; + result.config.use_ssl = true; /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. * Only set if it's non-zero. This helps when we include multiple * parameters, like -S and -C combinations */ - ssl_version = CURL_SSLVERSION_DEFAULT; - if (c == 'S' && optarg != NULL) { + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; + if (option_index == 'S' && optarg != NULL) { char *plus_ptr = strchr(optarg, '+'); if (plus_ptr) { - got_plus = 1; + got_plus = true; *plus_ptr = '\0'; } if (optarg[0] == '2') { - ssl_version = CURL_SSLVERSION_SSLv2; + result.config.ssl_version = CURL_SSLVERSION_SSLv2; } else if (optarg[0] == '3') { - ssl_version = CURL_SSLVERSION_SSLv3; + result.config.ssl_version = CURL_SSLVERSION_SSLv3; } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - ssl_version = CURL_SSLVERSION_TLSv1_0; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ else if (!strcmp(optarg, "1.1")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - ssl_version = CURL_SSLVERSION_TLSv1_1; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ else if (!strcmp(optarg, "1.2")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - ssl_version = CURL_SSLVERSION_TLSv1_2; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ else if (!strcmp(optarg, "1.3")) # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) - ssl_version = CURL_SSLVERSION_TLSv1_3; + result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; # else - ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ else { usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)")); @@ -1667,40 +1620,40 @@ bool process_arguments(int argc, char **argv) { } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) if (got_plus) { - switch (ssl_version) { + switch (result.config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_0: - ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; + result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; break; } } else { - switch (ssl_version) { + switch (result.config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; break; case CURL_SSLVERSION_TLSv1_1: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; break; case CURL_SSLVERSION_TLSv1_0: - ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; break; } } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), ssl_version); + printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); } if (!specify_port) { - server_port = HTTPS_PORT; + result.config.server_port = HTTPS_PORT; } - break; + } break; #else /* LIBCURL_FEATURE_SSL */ /* -C -J and -K fall through to here without SSL */ usage4(_("Invalid option - SSL is not available")); @@ -1713,50 +1666,53 @@ bool process_arguments(int argc, char **argv) { if (!is_intnonneg(optarg)) { usage2(_("Invalid max_redirs count"), optarg); } else { - max_depth = atoi(optarg); + result.config.max_depth = atoi(optarg); } break; case 'f': /* onredirect */ if (!strcmp(optarg, "ok")) { - onredirect = STATE_OK; + result.config.onredirect = STATE_OK; } else if (!strcmp(optarg, "warning")) { - onredirect = STATE_WARNING; + result.config.onredirect = STATE_WARNING; } else if (!strcmp(optarg, "critical")) { - onredirect = STATE_CRITICAL; + result.config.onredirect = STATE_CRITICAL; } else if (!strcmp(optarg, "unknown")) { - onredirect = STATE_UNKNOWN; + result.config.onredirect = STATE_UNKNOWN; } else if (!strcmp(optarg, "follow")) { - onredirect = STATE_DEPENDENT; + result.config.onredirect = STATE_DEPENDENT; } else if (!strcmp(optarg, "stickyport")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST | STICKY_PORT; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.followsticky = STICKY_HOST | STICKY_PORT; } else if (!strcmp(optarg, "sticky")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.followsticky = STICKY_HOST; } else if (!strcmp(optarg, "follow")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.followsticky = STICKY_NONE; } else if (!strcmp(optarg, "curl")) { - onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL; + result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_LIBCURL; } else { usage2(_("Invalid onredirect option"), optarg); } if (verbose >= 2) { - printf(_("* Following redirects set to %s\n"), state_text(onredirect)); + printf(_("* Following redirects set to %s\n"), state_text(result.config.onredirect)); } break; case 'd': /* string or substring */ - strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); - header_expect[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.header_expect, optarg, MAX_INPUT_BUFFER - 1); + result.config.header_expect[MAX_INPUT_BUFFER - 1] = 0; break; case 's': /* string or substring */ - strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1); - string_expect[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.string_expect, optarg, MAX_INPUT_BUFFER - 1); + result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0; break; case 'e': /* string or substring */ - strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1); - server_expect[MAX_INPUT_BUFFER - 1] = 0; - server_expect_yn = 1; + strncpy(result.config.server_expect, optarg, MAX_INPUT_BUFFER - 1); + result.config.server_expect[MAX_INPUT_BUFFER - 1] = 0; + result.config.server_expect_yn = true; break; case 'T': /* Content-type */ - http_content_type = strdup(optarg); + result.config.http_content_type = strdup(optarg); break; case 'l': /* linespan */ cflags &= ~REG_NEWLINE; @@ -1765,23 +1721,25 @@ bool process_arguments(int argc, char **argv) { cflags |= REG_ICASE; // fall through case 'r': /* regex */ - strncpy(regexp, optarg, MAX_RE_SIZE - 1); - regexp[MAX_RE_SIZE - 1] = 0; - errcode = regcomp(&preg, regexp, cflags); + strncpy(result.config.regexp, optarg, MAX_RE_SIZE - 1); + result.config.regexp[MAX_RE_SIZE - 1] = 0; + regex_t preg; + int errcode = regcomp(&preg, result.config.regexp, cflags); if (errcode != 0) { (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); printf(_("Could Not Compile Regular Expression: %s"), errbuf); - return false; + result.errorcode = ERROR; + return result; } break; case INVERT_REGEX: - invert_regex = true; + result.config.invert_regex = true; break; case STATE_REGEX: if (!strcasecmp(optarg, "critical")) { - state_regex = STATE_CRITICAL; + result.config.state_regex = STATE_CRITICAL; } else if (!strcasecmp(optarg, "warning")) { - state_regex = STATE_WARNING; + result.config.state_regex = STATE_WARNING; } else { usage2(_("Invalid state-regex option"), optarg); } @@ -1806,7 +1764,7 @@ bool process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - min_page_len = atoi(tmp); + result.config.min_page_len = atoi(tmp); } tmp = strtok(NULL, ":"); @@ -1814,52 +1772,52 @@ bool process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - max_page_len = atoi(tmp); + result.config.max_page_len = atoi(tmp); } } else { - min_page_len = atoi(optarg); + result.config.min_page_len = atoi(optarg); } break; } case 'N': /* no-body */ - no_body = true; + result.config.no_body = true; break; case 'M': /* max-age */ { - int L = strlen(optarg); - if (L && optarg[L - 1] == 'm') { - maximum_age = atoi(optarg) * 60; - } else if (L && optarg[L - 1] == 'h') { - maximum_age = atoi(optarg) * 60 * 60; - } else if (L && optarg[L - 1] == 'd') { - maximum_age = atoi(optarg) * 60 * 60 * 24; - } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) { - maximum_age = atoi(optarg); + size_t option_length = strlen(optarg); + if (option_length && optarg[option_length - 1] == 'm') { + result.config.maximum_age = atoi(optarg) * 60; + } else if (option_length && optarg[option_length - 1] == 'h') { + result.config.maximum_age = atoi(optarg) * 60 * 60; + } else if (option_length && optarg[option_length - 1] == 'd') { + result.config.maximum_age = atoi(optarg) * 60 * 60 * 24; + } else if (option_length && (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) { + result.config.maximum_age = atoi(optarg); } else { fprintf(stderr, "unparsable max-age: %s\n", optarg); exit(STATE_WARNING); } if (verbose >= 2) { - printf("* Maximal age of document set to %d seconds\n", maximum_age); + printf("* Maximal age of document set to %d seconds\n", result.config.maximum_age); } } break; case 'E': /* show extended perfdata */ - show_extended_perfdata = true; + result.config.show_extended_perfdata = true; break; case 'B': /* print body content after status line */ - show_body = true; + result.config.show_body = true; break; case HTTP_VERSION_OPTION: - curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_http_version = CURL_HTTP_VERSION_NONE; if (strcmp(optarg, "1.0") == 0) { - curl_http_version = CURL_HTTP_VERSION_1_0; + result.config.curl_http_version = CURL_HTTP_VERSION_1_0; } else if (strcmp(optarg, "1.1") == 0) { - curl_http_version = CURL_HTTP_VERSION_1_1; + result.config.curl_http_version = CURL_HTTP_VERSION_1_1; } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) - curl_http_version = CURL_HTTP_VERSION_2_0; + result.config.curl_http_version = CURL_HTTP_VERSION_2_0; #else - curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); @@ -1867,13 +1825,13 @@ bool process_arguments(int argc, char **argv) { } break; case AUTOMATIC_DECOMPRESSION: - automatic_decompression = true; + result.config.automatic_decompression = true; break; case COOKIE_JAR: - cookie_jar_file = optarg; + result.config.cookie_jar_file = optarg; break; case HAPROXY_PROTOCOL: - haproxy_protocol = true; + result.config.haproxy_protocol = true; break; case '?': /* print short usage statement if args not parsable */ @@ -1882,82 +1840,82 @@ bool process_arguments(int argc, char **argv) { } } - c = optind; + int c = optind; - if (server_address == NULL && c < argc) { - server_address = strdup(argv[c++]); + if (result.config.server_address == NULL && c < argc) { + result.config.server_address = strdup(argv[c++]); } - if (host_name == NULL && c < argc) { - host_name = strdup(argv[c++]); + if (result.config.host_name == NULL && c < argc) { + result.config.host_name = strdup(argv[c++]); } - if (server_address == NULL) { - if (host_name == NULL) { + if (result.config.server_address == NULL) { + if (result.config.host_name == NULL) { usage4(_("You must specify a server address or host name")); } else { - server_address = strdup(host_name); + result.config.server_address = strdup(result.config.host_name); } } - set_thresholds(&thlds, warning_thresholds, critical_thresholds); + set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); - if (critical_thresholds && thlds->critical->end > (double)socket_timeout) { - socket_timeout = (int)thlds->critical->end + 1; + if (critical_thresholds && result.config.thlds->critical->end > (double)result.config.socket_timeout) { + result.config.socket_timeout = (int)result.config.thlds->critical->end + 1; } if (verbose >= 2) { - printf("* Socket timeout set to %ld seconds\n", socket_timeout); + printf("* Socket timeout set to %ld seconds\n", result.config.socket_timeout); } - if (http_method == NULL) { - http_method = strdup("GET"); + if (result.config.http_method == NULL) { + result.config.http_method = strdup("GET"); } - if (client_cert && !client_privkey) { + if (result.config.client_cert && !result.config.client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); } - if (virtual_port == 0) { - virtual_port = server_port; + if (result.config.virtual_port == 0) { + result.config.virtual_port = result.config.server_port; } else { - if ((use_ssl && server_port == HTTPS_PORT) || (!use_ssl && server_port == HTTP_PORT)) { + if ((result.config.use_ssl && result.config.server_port == HTTPS_PORT) || + (!result.config.use_ssl && result.config.server_port == HTTP_PORT)) { if (!specify_port) { - server_port = virtual_port; + result.config.server_port = result.config.virtual_port; } } } - return true; + return result; } -char *perfd_time(double elapsed_time) { - return fperfdata("time", elapsed_time, "s", thlds->warning ? true : false, thlds->warning ? thlds->warning->end : 0, - thlds->critical ? true : false, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); +char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { + return fperfdata("time", elapsed_time, "s", thlds->warning, thlds->warning ? thlds->warning->end : 0, thlds->critical, + thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); } -char *perfd_time_connect(double elapsed_time_connect) { +char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_ssl(double elapsed_time_ssl) { +char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_headers(double elapsed_time_headers) { +char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_firstbyte(double elapsed_time_firstbyte) { +char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_time_transfer(double elapsed_time_transfer) { +char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); } -char *perfd_size(int page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0 ? true : false), min_page_len, (min_page_len > 0 ? true : false), 0, true, 0, - false, 0); +char *perfd_size(int page_len, int min_page_len) { + return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, true, 0, false, 0); } void print_help(void) { @@ -2259,11 +2217,6 @@ void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { /* TODO: where to put this, it's actually part of sstrings2 (logically)? */ const char *strrstr2(const char *haystack, const char *needle) { - int counter; - size_t len; - const char *prev_pos; - const char *pos; - if (haystack == NULL || needle == NULL) { return NULL; } @@ -2272,10 +2225,10 @@ const char *strrstr2(const char *haystack, const char *needle) { return NULL; } - counter = 0; - prev_pos = NULL; - pos = haystack; - len = strlen(needle); + int counter = 0; + const char *prev_pos = NULL; + const char *pos = haystack; + size_t len = strlen(needle); for (;;) { pos = strstr(pos, needle); if (pos == NULL) { @@ -2294,37 +2247,29 @@ const char *strrstr2(const char *haystack, const char *needle) { } int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { - char *first_line_end; - char *p; - size_t first_line_len; - char *pp; - const char *start; - char *first_line_buf; - /* find last start of a new header */ - start = strrstr2(buf, "\r\nHTTP/"); + const char *start = strrstr2(buf, "\r\nHTTP/"); if (start != NULL) { start += 2; buf = start; } - first_line_end = strstr(buf, "\r\n"); + char *first_line_end = strstr(buf, "\r\n"); if (first_line_end == NULL) { return -1; } - first_line_len = (size_t)(first_line_end - buf); + size_t first_line_len = (size_t)(first_line_end - buf); status_line->first_line = (char *)malloc(first_line_len + 1); if (status_line->first_line == NULL) { return -1; } memcpy(status_line->first_line, buf, first_line_len); status_line->first_line[first_line_len] = '\0'; - first_line_buf = strdup(status_line->first_line); + char *first_line_buf = strdup(status_line->first_line); /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ - - p = strtok(first_line_buf, "/"); + char *p = strtok(first_line_buf, "/"); if (p == NULL) { free(first_line_buf); return -1; @@ -2339,6 +2284,8 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) free(first_line_buf); return -1; } + + char *pp; if (strchr(p, '.') != NULL) { /* HTTP 1.x case */ @@ -2418,15 +2365,11 @@ char *get_header_value(const struct phr_header *headers, const size_t nof_header return NULL; } -int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) { - char *server_date = NULL; - char *document_date = NULL; - int date_result = STATE_OK; - curlhelp_statusline status_line; +int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; + curlhelp_statusline status_line; size_t msglen; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); @@ -2434,9 +2377,10 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); } - server_date = get_header_value(headers, nof_headers, "date"); - document_date = get_header_value(headers, nof_headers, "last-modified"); + char *server_date = get_header_value(headers, nof_headers, "date"); + char *document_date = get_header_value(headers, nof_headers, "last-modified"); + int date_result = STATE_OK; if (!server_date || !*server_date) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -2511,13 +2455,10 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D } int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) { - size_t content_length = 0; struct phr_header headers[255]; size_t nof_headers = 255; size_t msglen; - char *content_length_s = NULL; curlhelp_statusline status_line; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); @@ -2525,12 +2466,13 @@ int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_ die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); } - content_length_s = get_header_value(headers, nof_headers, "content-length"); + char *content_length_s = get_header_value(headers, nof_headers, "content-length"); if (!content_length_s) { return header_buf->buflen + body_buf->buflen; } + content_length_s += strspn(content_length_s, " \t"); - content_length = atoi(content_length_s); + size_t content_length = atoi(content_length_s); if (content_length != body_buf->buflen) { /* TODO: should we warn if the actual and the reported body length don't match? */ } @@ -2544,22 +2486,19 @@ int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_ /* TODO: is there a better way in libcurl to check for the SSL library? */ curlhelp_ssl_library curlhelp_get_ssl_library(void) { - curl_version_info_data *version_data; - char *ssl_version; - char *library; curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; - version_data = curl_version_info(CURLVERSION_NOW); + curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW); if (version_data == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; } - ssl_version = strdup(version_data->ssl_version); + char *ssl_version = strdup(version_data->ssl_version); if (ssl_version == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; } - library = strtok(ssl_version, "/"); + char *library = strtok(ssl_version, "/"); if (library == NULL) { return CURLHELP_SSL_LIBRARY_UNKNOWN; } @@ -2602,21 +2541,18 @@ const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) { #ifdef LIBCURL_FEATURE_SSL # ifndef USE_OPENSSL time_t parse_cert_date(const char *s) { - struct tm tm; - time_t date; - char *res; - if (!s) { return -1; } /* Jan 17 14:25:12 2020 GMT */ - res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); + struct tm tm; + char *res = strptime(s, "%Y-%m-%d %H:%M:%S GMT", &tm); /* Sep 11 12:00:00 2020 GMT */ if (res == NULL) { strptime(s, "%Y %m %d %H:%M:%S GMT", &tm); } - date = mktime(&tm); + time_t date = mktime(&tm); return date; } @@ -2625,7 +2561,6 @@ time_t parse_cert_date(const char *s) { * OpenSSL could be this function */ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { - int i; struct curl_slist *slist; int cname_found = 0; char *start_date_str = NULL; @@ -2643,7 +2578,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ printf("**** REQUEST CERTIFICATES ****\n"); } - for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { + for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { /* find first common name in subject, * TODO: check alternative subjects for diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h new file mode 100644 index 00000000..9085fbbb --- /dev/null +++ b/plugins/check_curl.d/config.h @@ -0,0 +1,141 @@ +#pragma once + +#include "../../config.h" +#include "common.h" +#include "states.h" +#include "thresholds.h" +#include +#include +#include "curl/curl.h" + +enum { + MAX_RE_SIZE = 1024, + HTTP_PORT = 80, + HTTPS_PORT = 443, + MAX_PORT = 65535, + DEFAULT_MAX_REDIRS = 15 +}; + +enum { + FOLLOW_HTTP_CURL = 0, + FOLLOW_LIBCURL = 1 +}; + +enum { + STICKY_NONE = 0, + STICKY_HOST = 1, + STICKY_PORT = 2 +}; + +#define HTTP_EXPECT "HTTP/" +#define DEFAULT_BUFFER_SIZE 2048 +#define DEFAULT_SERVER_URL "/" + +typedef struct { + char *server_address; + unsigned short server_port; + unsigned short virtual_port; + char *host_name; + char *server_url; + + bool automatic_decompression; + bool haproxy_protocol; + char *client_cert; + char *client_privkey; + char *ca_cert; + bool use_ssl; + int ssl_version; + char *http_method; + char user_agent[DEFAULT_BUFFER_SIZE]; + char **http_opt_headers; + int http_opt_headers_count; + char *http_post_data; + int max_depth; + char *http_content_type; + long socket_timeout; + char user_auth[MAX_INPUT_BUFFER]; + char proxy_auth[MAX_INPUT_BUFFER]; + int followmethod; + int followsticky; + bool no_body; + int curl_http_version; + char *cookie_jar_file; + + int maximum_age; + char regexp[MAX_RE_SIZE]; + int state_regex; + bool invert_regex; + bool verify_peer_and_host; + bool check_cert; + bool continue_after_check_cert; + int days_till_exp_warn; + int days_till_exp_crit; + thresholds *thlds; + int min_page_len; + int max_page_len; + char server_expect[MAX_INPUT_BUFFER]; + bool server_expect_yn; + char string_expect[MAX_INPUT_BUFFER]; + char header_expect[MAX_INPUT_BUFFER]; + int onredirect; + + bool show_extended_perfdata; + bool show_body; + bool display_html; +} check_curl_config; + +check_curl_config check_curl_config_init() { + check_curl_config tmp = { + .server_address = NULL, + .server_port = HTTP_PORT, + .virtual_port = 0, + .host_name = NULL, + .server_url = strdup(DEFAULT_SERVER_URL), + + .automatic_decompression = false, + .haproxy_protocol = false, + .client_cert = NULL, + .client_privkey = NULL, + .ca_cert = NULL, + .use_ssl = false, + .ssl_version = CURL_SSLVERSION_DEFAULT, + .http_method = NULL, + .user_agent = {}, + .http_opt_headers = NULL, + .http_opt_headers_count = 0, + .http_post_data = NULL, + .max_depth = DEFAULT_MAX_REDIRS, + .http_content_type = NULL, + .socket_timeout = DEFAULT_SOCKET_TIMEOUT, + .user_auth = "", + .proxy_auth = "", + .followmethod = FOLLOW_HTTP_CURL, + .followsticky = STICKY_NONE, + .no_body = false, + .curl_http_version = CURL_HTTP_VERSION_NONE, + .cookie_jar_file = NULL, + + .maximum_age = -1, + .regexp = {}, + .state_regex = STATE_CRITICAL, + .invert_regex = false, + .verify_peer_and_host = false, + .check_cert = false, + .continue_after_check_cert = false, + .days_till_exp_warn = 0, + .days_till_exp_crit = 0, + .thlds = NULL, + .min_page_len = 0, + .max_page_len = 0, + .server_expect = HTTP_EXPECT, + .server_expect_yn = false, + .string_expect = "", + .header_expect = "", + .onredirect = STATE_OK, + + .show_extended_perfdata = false, + .show_body = false, + .display_html = false, + }; + return tmp; +} -- cgit v1.2.3-74-g34f1 From 37c543e2b20657a8f4c120ba6b52e8e605a417bb Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:50:39 +0100 Subject: Remove check_nwstat check_nwstat is a plugin which was used to determine the health of things on Novel machines. Since Novel is quite dead (even more so the product, this can be removed and this commit does just that to reduce ressource usage. --- .github/monitoring-plugins.spec | 14 - .gitignore | 1 - REQUIREMENTS | 4 - plugins/Makefile.am | 3 +- plugins/check_nwstat.c | 1527 --------------------------------------- 5 files changed, 1 insertion(+), 1548 deletions(-) delete mode 100644 plugins/check_nwstat.c diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec index 64ee34f2..10799128 100644 --- a/.github/monitoring-plugins.spec +++ b/.github/monitoring-plugins.spec @@ -191,7 +191,6 @@ Requires: %{name}-nt Requires: %{name}-ntp Requires: %{name}-ntp_peer Requires: %{name}-ntp_time -Requires: %{name}-nwstat Requires: %{name}-oracle Requires: %{name}-pgsql Requires: %{name}-ping @@ -702,19 +701,6 @@ Provides check_ntp_time of the Monitoring Plugins. -# check_nwstat -%package nwstat -Summary: Monitoring Plugins - check_nwstat -Requires: %{name} = %{version}-%{release} - -%description nwstat -Provides check_nwstat of the Monitoring Plugins. - -%files nwstat -%{plugindir}/check_nwstat - - - # check_oracle %package oracle Summary: Monitoring Plugins - check_oracle diff --git a/.gitignore b/.gitignore index f9cb37e4..7f79265f 100644 --- a/.gitignore +++ b/.gitignore @@ -177,7 +177,6 @@ NP-VERSION-FILE /plugins/check_ntp /plugins/check_ntp_peer /plugins/check_ntp_time -/plugins/check_nwstat /plugins/check_pgsql /plugins/check_ping /plugins/check_pop diff --git a/REQUIREMENTS b/REQUIREMENTS index f3b1c01d..551fdb1a 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -87,10 +87,6 @@ check_ifstatus/check_ifoperstatus - Requires Net::SNMP perl module http://www.perl.com/CPAN/modules/by-authors/id/D/DT/DTOWN/ -check_nwstat: - - Requires MRTGEXT NLM for Novell Servers - http://forge.novell.com/modules/xfmod/project/?mrtgext - check_nt: - Requires NSClient to run on the NT server to monitor http://nsclient.ready2run.nl/ diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 6c582a15..7c404a3b 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -27,7 +27,7 @@ MATHLIBS = @MATHLIBS@ #AM_CFLAGS = -Wall libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http check_load \ - check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_nwstat check_ping \ + check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \ check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ check_ups check_users negate \ urlize @EXTRAS@ @@ -131,7 +131,6 @@ check_nagios_LDADD = $(BASEOBJS) check_nt_LDADD = $(NETLIBS) check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) -check_nwstat_LDADD = $(NETLIBS) check_pgsql_LDADD = $(NETLIBS) $(PGLIBS) check_ping_LDADD = $(NETLIBS) check_procs_LDADD = $(BASEOBJS) diff --git a/plugins/check_nwstat.c b/plugins/check_nwstat.c deleted file mode 100644 index 176dfbc8..00000000 --- a/plugins/check_nwstat.c +++ /dev/null @@ -1,1527 +0,0 @@ -/***************************************************************************** - * - * Monitoring check_nwstat plugin - * - * License: GPL - * Copyright (c) 2000-2024 Monitoring Plugins Development Team - * - * Description: - * - * This file contains the check_nwstat plugin - * - * This plugin attempts to contact the MRTGEXT NLM running on a - * Novell server to gather the requested system information. - * - * - * 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 . - * - * - *****************************************************************************/ - -const char *progname = "check_nwstat"; -const char *copyright = "2000-2024"; -const char *email = "devel@monitoring-plugins.org"; - -#include "common.h" -#include "netutils.h" -#include "utils.h" - -enum checkvar { - NONE, - LOAD1, /* check 1 minute CPU load */ - LOAD5, /* check 5 minute CPU load */ - LOAD15, /* check 15 minute CPU load */ - CONNS, /* check number of connections */ - VPF, /* check % free space on volume */ - VMF, /* check MB free space on volume */ - VMU, /* check MB used space on volume */ - VPU, /* check % used space on volume */ - VMP, /* check MB purgeable space on volume */ - VKF, /* check KB free space on volume */ - LTCH, /* check long-term cache hit percentage */ - CBUFF, /* check total cache buffers */ - CDBUFF, /* check dirty cache buffers */ - LRUM, /* check LRU sitting time in minutes */ - DSDB, /* check to see if DS Database is open */ - LOGINS, /* check to see if logins are enabled */ - NRMH, /* check to see NRM Health Status */ - PUPRB, /* check % of used packet receive buffers */ - UPRB, /* check used packet receive buffers */ - SAPENTRIES, /* check SAP entries */ - OFILES, /* check number of open files */ - VKP, /* check KB purgeable space on volume */ - VPP, /* check % purgeable space on volume */ - VKNP, /* check KB not yet purgeable space on volume */ - VPNP, /* check % not yet purgeable space on volume */ - ABENDS, /* check abended thread count */ - CSPROCS, /* check number of current service processes */ - TSYNC, /* check timesync status 0=no 1=yes in sync to the network */ - LRUS, /* check LRU sitting time in seconds */ - DCB, /* check dirty cache buffers as a percentage of the total */ - TCB, /* check total cache buffers as a percentage of the original */ - DSVER, /* check NDS version */ - UPTIME, /* check server uptime */ - NLM, /* check NLM loaded */ - NRMP, /* check NRM Process Values */ - NRMM, /* check NRM Memory Values */ - NRMS, /* check NRM Values */ - NSS1, /* check Statistics from _Admin:Manage_NSS\GeneralStats.xml */ - NSS2, /* check Statistics from _Admin:Manage_NSS\BufferCache.xml */ - NSS3, /* check statistics from _Admin:Manage_NSS\NameCache.xml */ - NSS4, /* check statistics from _Admin:Manage_NSS\FileStats.xml */ - NSS5, /* check statistics from _Admin:Manage_NSS\ObjectCache.xml */ - NSS6, /* check statistics from _Admin:Manage_NSS\Thread.xml */ - NSS7 /* check statistics from _Admin:Manage_NSS\AuthorizationCache.xml */ -}; - -enum { - PORT = 9999 -}; - -static char *server_address = NULL; -static char *volume_name = NULL; -static char *nlm_name = NULL; -static char *nrmp_name = NULL; -static char *nrmm_name = NULL; -static char *nrms_name = NULL; -static char *nss1_name = NULL; -static char *nss2_name = NULL; -static char *nss3_name = NULL; -static char *nss4_name = NULL; -static char *nss5_name = NULL; -static char *nss6_name = NULL; -static char *nss7_name = NULL; -static int server_port = PORT; -static unsigned long warning_value = 0L; -static unsigned long critical_value = 0L; -static bool check_warning_value = false; -static bool check_critical_value = false; -static bool check_netware_version = false; -static enum checkvar vars_to_check = NONE; -static int sap_number = -1; - -static int process_arguments(int /*argc*/, char ** /*argv*/); -static void print_help(void); -void print_usage(void); - -int main(int argc, char **argv) { - int result = STATE_UNKNOWN; - int sd; - char *send_buffer = NULL; - char recv_buffer[MAX_INPUT_BUFFER]; - char *output_message = NULL; - char *temp_buffer = NULL; - char *netware_version = NULL; - - int time_sync_status = 0; - int nrm_health_status = 0; - unsigned long total_cache_buffers = 0; - unsigned long dirty_cache_buffers = 0; - unsigned long open_files = 0; - unsigned long abended_threads = 0; - unsigned long max_service_processes = 0; - unsigned long current_service_processes = 0; - unsigned long free_disk_space = 0L; - unsigned long nrmp_value = 0L; - unsigned long nrmm_value = 0L; - unsigned long nrms_value = 0L; - unsigned long nss1_value = 0L; - unsigned long nss2_value = 0L; - unsigned long nss3_value = 0L; - unsigned long nss4_value = 0L; - unsigned long nss5_value = 0L; - unsigned long nss6_value = 0L; - unsigned long nss7_value = 0L; - unsigned long total_disk_space = 0L; - unsigned long used_disk_space = 0L; - unsigned long percent_used_disk_space = 0L; - unsigned long purgeable_disk_space = 0L; - unsigned long non_purgeable_disk_space = 0L; - unsigned long percent_free_space = 0; - unsigned long percent_purgeable_space = 0; - unsigned long percent_non_purgeable_space = 0; - unsigned long current_connections = 0L; - unsigned long utilization = 0L; - unsigned long cache_hits = 0; - unsigned long cache_buffers = 0L; - unsigned long lru_time = 0L; - unsigned long max_packet_receive_buffers = 0; - unsigned long used_packet_receive_buffers = 0; - unsigned long percent_used_packet_receive_buffers = 0L; - unsigned long sap_entries = 0; - char uptime[MAX_INPUT_BUFFER]; - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - /* Parse extra opts if any */ - argv = np_extra_opts(&argc, argv, progname); - - if (process_arguments(argc, argv) == ERROR) - usage4(_("Could not parse arguments")); - - /* initialize alarm signal handling */ - signal(SIGALRM, socket_timeout_alarm_handler); - - /* set socket timeout */ - alarm(socket_timeout); - - /* open connection */ - my_tcp_connect(server_address, server_port, &sd); - - /* get OS version string */ - if (check_netware_version) { - send_buffer = strdup("S19\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - if (!strcmp(recv_buffer, "-1\n")) - netware_version = strdup(""); - else { - recv_buffer[strlen(recv_buffer) - 1] = 0; - xasprintf(&netware_version, _("NetWare %s: "), recv_buffer); - } - } else - netware_version = strdup(""); - - /* check CPU load */ - if (vars_to_check == LOAD1 || vars_to_check == LOAD5 || vars_to_check == LOAD15) { - - switch (vars_to_check) { - case LOAD1: - temp_buffer = strdup("1"); - break; - case LOAD5: - temp_buffer = strdup("5"); - break; - default: - temp_buffer = strdup("15"); - break; - } - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "UTIL%s\r\n", temp_buffer); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - utilization = strtoul(recv_buffer, NULL, 10); - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("UPTIME\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - recv_buffer[strlen(recv_buffer) - 1] = 0; - sprintf(uptime, _("Up %s,"), recv_buffer); - - if (check_critical_value && utilization >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && utilization >= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("Load %s - %s %s-min load average = %lu%%|load%s=%lu;%lu;%lu;0;100"), state_text(result), uptime, - temp_buffer, utilization, temp_buffer, utilization, warning_value, critical_value); - - /* check number of user connections */ - } else if (vars_to_check == CONNS) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("CONNECT\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - current_connections = strtoul(recv_buffer, NULL, 10); - - if (check_critical_value && current_connections >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && current_connections >= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("Conns %s - %lu current connections|Conns=%lu;%lu;%lu;;"), state_text(result), current_connections, - current_connections, warning_value, critical_value); - - /* check % long term cache hits */ - } else if (vars_to_check == LTCH) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S1\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - cache_hits = atoi(recv_buffer); - - if (check_critical_value && cache_hits <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && cache_hits <= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("%s: Long term cache hits = %lu%%"), state_text(result), cache_hits); - - /* check cache buffers */ - } else if (vars_to_check == CBUFF) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S2\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - cache_buffers = strtoul(recv_buffer, NULL, 10); - - if (check_critical_value && cache_buffers <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && cache_buffers <= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("%s: Total cache buffers = %lu|Cachebuffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers, - cache_buffers, warning_value, critical_value); - - /* check dirty cache buffers */ - } else if (vars_to_check == CDBUFF) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S3\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - cache_buffers = strtoul(recv_buffer, NULL, 10); - - if (check_critical_value && cache_buffers >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && cache_buffers >= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("%s: Dirty cache buffers = %lu|Dirty-Cache-Buffers=%lu;%lu;%lu;;"), state_text(result), cache_buffers, - cache_buffers, warning_value, critical_value); - - /* check LRU sitting time in minutes */ - } else if (vars_to_check == LRUM) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S5\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - lru_time = strtoul(recv_buffer, NULL, 10); - - if (check_critical_value && lru_time <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && lru_time <= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("%s: LRU sitting time = %lu minutes"), state_text(result), lru_time); - - /* check KB free space on volume */ - } else if (vars_to_check == VKF) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKF%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - } else { - free_disk_space = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && free_disk_space <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && free_disk_space <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s%lu KB free on volume %s|KBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), - free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value); - } - - /* check MB free space on volume */ - } else if (vars_to_check == VMF) { - - xasprintf(&send_buffer, "VMF%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - } else { - free_disk_space = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && free_disk_space <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && free_disk_space <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s%lu MB free on volume %s|MBFree%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), - free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value); - } - /* check MB used space on volume */ - } else if (vars_to_check == VMU) { - - xasprintf(&send_buffer, "VMU%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - } else { - free_disk_space = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && free_disk_space <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && free_disk_space <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s%lu MB used on volume %s|MBUsed%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), - free_disk_space, volume_name, volume_name, free_disk_space, warning_value, critical_value); - } - /* check % used space on volume */ - } else if (vars_to_check == VPU) { - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - asprintf(&send_buffer, "VMU%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - asprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - - } else { - used_disk_space = strtoul(recv_buffer, NULL, 10); - close(sd); - my_tcp_connect(server_address, server_port, &sd); - /* get total volume in MB */ - asprintf(&send_buffer, "VMS%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - total_disk_space = strtoul(recv_buffer, NULL, 10); - /* calculate percent used on volume */ - percent_used_disk_space = (unsigned long)(((double)used_disk_space / (double)total_disk_space) * 100.0); - - if (check_critical_value && percent_used_disk_space >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && percent_used_disk_space >= warning_value) - result = STATE_WARNING; - - asprintf(&output_message, _("%lu MB (%lu%%) used on volume %s - total %lu MB|Used space in percent on %s=%lu;%lu;%lu;0;100"), - used_disk_space, percent_used_disk_space, volume_name, total_disk_space, volume_name, percent_used_disk_space, - warning_value, critical_value); - } - - /* check % free space on volume */ - } else if (vars_to_check == VPF) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKF%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - - } else { - - free_disk_space = strtoul(recv_buffer, NULL, 10); - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKS%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - total_disk_space = strtoul(recv_buffer, NULL, 10); - - percent_free_space = (unsigned long)(((double)free_disk_space / (double)total_disk_space) * 100.0); - - if (check_critical_value && percent_free_space <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && percent_free_space <= warning_value) - result = STATE_WARNING; - free_disk_space /= 1024; - total_disk_space /= 1024; - xasprintf(&output_message, _("%lu MB (%lu%%) free on volume %s - total %lu MB|FreeMB%s=%lu;%lu;%lu;0;100"), free_disk_space, - percent_free_space, volume_name, total_disk_space, volume_name, percent_free_space, warning_value, critical_value); - } - - /* check to see if DS Database is open or closed */ - } else if (vars_to_check == DSDB) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S11\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - if (atoi(recv_buffer) == 1) - result = STATE_OK; - else - result = STATE_WARNING; - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S13\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - temp_buffer = strtok(recv_buffer, "\r\n"); - - xasprintf(&output_message, _("Directory Services Database is %s (DS version %s)"), (result == STATE_OK) ? "open" : "closed", - temp_buffer); - - /* check to see if logins are enabled */ - } else if (vars_to_check == LOGINS) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S12\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - if (atoi(recv_buffer) == 1) - result = STATE_OK; - else - result = STATE_WARNING; - - xasprintf(&output_message, _("Logins are %s"), (result == STATE_OK) ? _("enabled") : _("disabled")); - - /* check NRM Health Status Summary*/ - } else if (vars_to_check == NRMH) { - - xasprintf(&send_buffer, "NRMH\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - nrm_health_status = atoi(recv_buffer); - - if (nrm_health_status == 2) { - result = STATE_OK; - xasprintf(&output_message, _("CRITICAL - NRM Status is bad!")); - } else { - if (nrm_health_status == 1) { - result = STATE_WARNING; - xasprintf(&output_message, _("Warning - NRM Status is suspect!")); - } - - xasprintf(&output_message, _("OK - NRM Status is good!")); - } - - /* check packet receive buffers */ - } else if (vars_to_check == UPRB || vars_to_check == PUPRB) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S15\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - used_packet_receive_buffers = atoi(recv_buffer); - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S16\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - max_packet_receive_buffers = atoi(recv_buffer); - - percent_used_packet_receive_buffers = - (unsigned long)(((double)used_packet_receive_buffers / (double)max_packet_receive_buffers) * 100.0); - - if (vars_to_check == UPRB) { - if (check_critical_value && used_packet_receive_buffers >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && used_packet_receive_buffers >= warning_value) - result = STATE_WARNING; - } else { - if (check_critical_value && percent_used_packet_receive_buffers >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && percent_used_packet_receive_buffers >= warning_value) - result = STATE_WARNING; - } - - xasprintf(&output_message, _("%lu of %lu (%lu%%) packet receive buffers used"), used_packet_receive_buffers, - max_packet_receive_buffers, percent_used_packet_receive_buffers); - - /* check SAP table entries */ - } else if (vars_to_check == SAPENTRIES) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - if (sap_number == -1) - xasprintf(&send_buffer, "S9\r\n"); - else - xasprintf(&send_buffer, "S9.%d\r\n", sap_number); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - sap_entries = atoi(recv_buffer); - - if (check_critical_value && sap_entries >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && sap_entries >= warning_value) - result = STATE_WARNING; - - if (sap_number == -1) - xasprintf(&output_message, _("%lu entries in SAP table"), sap_entries); - else - xasprintf(&output_message, _("%lu entries in SAP table for SAP type %d"), sap_entries, sap_number); - - /* check KB purgeable space on volume */ - } else if (vars_to_check == VKP) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKP%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - } else { - purgeable_disk_space = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && purgeable_disk_space >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && purgeable_disk_space >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s%lu KB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), - purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value); - } - /* check MB purgeable space on volume */ - } else if (vars_to_check == VMP) { - - xasprintf(&send_buffer, "VMP%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - } else { - purgeable_disk_space = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && purgeable_disk_space >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && purgeable_disk_space >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s%lu MB purgeable on volume %s|Purge%s=%lu;%lu;%lu;;"), (result == STATE_OK) ? "" : _("Only "), - purgeable_disk_space, volume_name, volume_name, purgeable_disk_space, warning_value, critical_value); - } - - /* check % purgeable space on volume */ - } else if (vars_to_check == VPP) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKP%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - - } else { - - purgeable_disk_space = strtoul(recv_buffer, NULL, 10); - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKS%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - total_disk_space = strtoul(recv_buffer, NULL, 10); - - percent_purgeable_space = (unsigned long)(((double)purgeable_disk_space / (double)total_disk_space) * 100.0); - - if (check_critical_value && percent_purgeable_space >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && percent_purgeable_space >= warning_value) - result = STATE_WARNING; - purgeable_disk_space /= 1024; - xasprintf(&output_message, _("%lu MB (%lu%%) purgeable on volume %s|Purgeable%s=%lu;%lu;%lu;0;100"), purgeable_disk_space, - percent_purgeable_space, volume_name, volume_name, percent_purgeable_space, warning_value, critical_value); - } - - /* check KB not yet purgeable space on volume */ - } else if (vars_to_check == VKNP) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKNP%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - } else { - non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && non_purgeable_disk_space >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && non_purgeable_disk_space >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s%lu KB not yet purgeable on volume %s"), (result == STATE_OK) ? "" : _("Only "), - non_purgeable_disk_space, volume_name); - } - - /* check % not yet purgeable space on volume */ - } else if (vars_to_check == VPNP) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKNP%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - - xasprintf(&output_message, _("CRITICAL - Volume '%s' does not exist!"), volume_name); - result = STATE_CRITICAL; - - } else { - - non_purgeable_disk_space = strtoul(recv_buffer, NULL, 10); - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "VKS%s\r\n", volume_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - total_disk_space = strtoul(recv_buffer, NULL, 10); - - percent_non_purgeable_space = (unsigned long)(((double)non_purgeable_disk_space / (double)total_disk_space) * 100.0); - - if (check_critical_value && percent_non_purgeable_space >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && percent_non_purgeable_space >= warning_value) - result = STATE_WARNING; - purgeable_disk_space /= 1024; - xasprintf(&output_message, _("%lu MB (%lu%%) not yet purgeable on volume %s"), non_purgeable_disk_space, - percent_non_purgeable_space, volume_name); - } - - /* check # of open files */ - } else if (vars_to_check == OFILES) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S18\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - open_files = atoi(recv_buffer); - - if (check_critical_value && open_files >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && open_files >= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("%lu open files|Openfiles=%lu;%lu;%lu;0,0"), open_files, open_files, warning_value, critical_value); - - /* check # of abended threads (Netware > 5.x only) */ - } else if (vars_to_check == ABENDS) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S17\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - abended_threads = atoi(recv_buffer); - - if (check_critical_value && abended_threads >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && abended_threads >= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("%lu abended threads|Abends=%lu;%lu;%lu;;"), abended_threads, abended_threads, warning_value, - critical_value); - - /* check # of current service processes (Netware 5.x only) */ - } else if (vars_to_check == CSPROCS) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S20\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - max_service_processes = atoi(recv_buffer); - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S21\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - current_service_processes = atoi(recv_buffer); - - if (check_critical_value && current_service_processes >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && current_service_processes >= warning_value) - result = STATE_WARNING; - - xasprintf(&output_message, _("%lu current service processes (%lu max)|Processes=%lu;%lu;%lu;0;%lu"), current_service_processes, - max_service_processes, current_service_processes, warning_value, critical_value, max_service_processes); - - /* check # Timesync Status */ - } else if (vars_to_check == TSYNC) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S22\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - time_sync_status = atoi(recv_buffer); - - if (time_sync_status == 0) { - result = STATE_CRITICAL; - xasprintf(&output_message, _("CRITICAL - Time not in sync with network!")); - } else { - xasprintf(&output_message, _("OK - Time in sync with network!")); - } - - /* check LRU sitting time in secondss */ - } else if (vars_to_check == LRUS) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S4\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - lru_time = strtoul(recv_buffer, NULL, 10); - - if (check_critical_value && lru_time <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && lru_time <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("LRU sitting time = %lu seconds"), lru_time); - - /* check % dirty cacheobuffers as a percentage of the total*/ - } else if (vars_to_check == DCB) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S6\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - dirty_cache_buffers = atoi(recv_buffer); - - if (check_critical_value && dirty_cache_buffers <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && dirty_cache_buffers <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("Dirty cache buffers = %lu%% of the total|DCB=%lu;%lu;%lu;0;100"), dirty_cache_buffers, - dirty_cache_buffers, warning_value, critical_value); - - /* check % total cache buffers as a percentage of the original*/ - } else if (vars_to_check == TCB) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - send_buffer = strdup("S7\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - total_cache_buffers = atoi(recv_buffer); - - if (check_critical_value && total_cache_buffers <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && total_cache_buffers <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("Total cache buffers = %lu%% of the original|TCB=%lu;%lu;%lu;0;100"), total_cache_buffers, - total_cache_buffers, warning_value, critical_value); - - } else if (vars_to_check == DSVER) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S13\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - recv_buffer[strlen(recv_buffer) - 1] = 0; - - xasprintf(&output_message, _("NDS Version %s"), recv_buffer); - - } else if (vars_to_check == UPTIME) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "UPTIME\r\n"); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - recv_buffer[sizeof(recv_buffer) - 1] = 0; - recv_buffer[strlen(recv_buffer) - 1] = 0; - - xasprintf(&output_message, _("Up %s"), recv_buffer); - - } else if (vars_to_check == NLM) { - - close(sd); - my_tcp_connect(server_address, server_port, &sd); - - xasprintf(&send_buffer, "S24:%s\r\n", nlm_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - recv_buffer[strlen(recv_buffer) - 1] = 0; - if (strcmp(recv_buffer, "-1")) { - xasprintf(&output_message, _("Module %s version %s is loaded"), nlm_name, recv_buffer); - } else { - result = STATE_CRITICAL; - xasprintf(&output_message, _("Module %s is not loaded"), nlm_name); - } - } else if (vars_to_check == NRMP) { - - xasprintf(&send_buffer, "NRMP:%s\r\n", nrmp_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmp_name); - result = STATE_CRITICAL; - } else { - nrmp_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nrmp_value <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nrmp_value <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmp_name, nrmp_value, nrmp_name, nrmp_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NRMM) { - - xasprintf(&send_buffer, "NRMM:%s\r\n", nrmm_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrmm_name); - result = STATE_CRITICAL; - } else { - nrmm_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nrmm_value <= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nrmm_value <= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrmm_name, nrmm_value, nrmm_name, nrmm_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NRMS) { - - xasprintf(&send_buffer, "NRMS:%s\r\n", nrms_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nrms_name); - result = STATE_CRITICAL; - } else { - nrms_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nrms_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nrms_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nrms_name, nrms_value, nrms_name, nrms_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NSS1) { - - xasprintf(&send_buffer, "NSS1:%s\r\n", nss1_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss1_name); - result = STATE_CRITICAL; - } else { - nss1_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nss1_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nss1_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss1_name, nss1_value, nss1_name, nss1_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NSS2) { - - xasprintf(&send_buffer, "NSS2:%s\r\n", nss2_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss2_name); - result = STATE_CRITICAL; - } else { - nss2_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nss2_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nss2_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss2_name, nss2_value, nss2_name, nss2_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NSS3) { - - xasprintf(&send_buffer, "NSS3:%s\r\n", nss3_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss3_name); - result = STATE_CRITICAL; - } else { - nss3_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nss3_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nss3_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss3_name, nss3_value, nss3_name, nss3_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NSS4) { - - xasprintf(&send_buffer, "NSS4:%s\r\n", nss4_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss4_name); - result = STATE_CRITICAL; - } else { - nss4_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nss4_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nss4_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss4_name, nss4_value, nss4_name, nss4_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NSS5) { - - xasprintf(&send_buffer, "NSS5:%s\r\n", nss5_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss5_name); - result = STATE_CRITICAL; - } else { - nss5_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nss5_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nss5_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss5_name, nss5_value, nss5_name, nss5_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NSS6) { - - xasprintf(&send_buffer, "NSS6:%s\r\n", nss6_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss6_name); - result = STATE_CRITICAL; - } else { - nss6_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nss6_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nss6_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss6_name, nss6_value, nss6_name, nss6_value, warning_value, - critical_value); - } - - } else if (vars_to_check == NSS7) { - - xasprintf(&send_buffer, "NSS7:%s\r\n", nss7_name); - result = send_tcp_request(sd, send_buffer, recv_buffer, sizeof(recv_buffer)); - if (result != STATE_OK) - return result; - - if (!strcmp(recv_buffer, "-1\n")) { - xasprintf(&output_message, _("CRITICAL - Value '%s' does not exist!"), nss7_name); - result = STATE_CRITICAL; - } else { - nss7_value = strtoul(recv_buffer, NULL, 10); - if (check_critical_value && nss7_value >= critical_value) - result = STATE_CRITICAL; - else if (check_warning_value && nss7_value >= warning_value) - result = STATE_WARNING; - xasprintf(&output_message, _("%s is %lu|%s=%lu;%lu;%lu;;"), nss7_name, nss7_value, nss7_name, nss7_value, warning_value, - critical_value); - } - - } else { - - output_message = strdup(_("Nothing to check!\n")); - result = STATE_UNKNOWN; - } - - close(sd); - - /* reset timeout */ - alarm(0); - - printf("%s%s\n", netware_version, output_message); - - return result; -} - -/* process command-line arguments */ -int process_arguments(int argc, char **argv) { - int c; - - int option = 0; - static struct option longopts[] = {{"port", required_argument, 0, 'p'}, {"timeout", required_argument, 0, 't'}, - {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, - {"variable", required_argument, 0, 'v'}, {"hostname", required_argument, 0, 'H'}, - {"osversion", no_argument, 0, 'o'}, {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; - - /* no options were supplied */ - if (argc < 2) - return ERROR; - - /* backwards compatibility */ - if (!is_option(argv[1])) { - server_address = argv[1]; - argv[1] = argv[0]; - argv = &argv[1]; - argc--; - } - - for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) - strcpy(argv[c], "-t"); - else if (strcmp("-wv", argv[c]) == 0) - strcpy(argv[c], "-w"); - else if (strcmp("-cv", argv[c]) == 0) - strcpy(argv[c], "-c"); - } - - while (1) { - c = getopt_long(argc, argv, "+hoVH:t:c:w:p:v:", longopts, &option); - - if (c == -1 || c == EOF || c == 1) - break; - - switch (c) { - case '?': /* print short usage statement if args not parsable */ - usage5(); - case 'h': /* help */ - print_help(); - exit(STATE_UNKNOWN); - case 'V': /* version */ - print_revision(progname, NP_VERSION); - exit(STATE_UNKNOWN); - case 'H': /* hostname */ - server_address = optarg; - break; - case 'o': /* display nos version */ - check_netware_version = true; - break; - case 'p': /* port */ - if (is_intnonneg(optarg)) - server_port = atoi(optarg); - else - die(STATE_UNKNOWN, _("Server port an integer\n")); - break; - case 'v': - if (strlen(optarg) < 3) - return ERROR; - if (!strcmp(optarg, "LOAD1")) - vars_to_check = LOAD1; - else if (!strcmp(optarg, "LOAD5")) - vars_to_check = LOAD5; - else if (!strcmp(optarg, "LOAD15")) - vars_to_check = LOAD15; - else if (!strcmp(optarg, "CONNS")) - vars_to_check = CONNS; - else if (!strcmp(optarg, "LTCH")) - vars_to_check = LTCH; - else if (!strcmp(optarg, "DCB")) - vars_to_check = DCB; - else if (!strcmp(optarg, "TCB")) - vars_to_check = TCB; - else if (!strcmp(optarg, "CBUFF")) - vars_to_check = CBUFF; - else if (!strcmp(optarg, "CDBUFF")) - vars_to_check = CDBUFF; - else if (!strcmp(optarg, "LRUM")) - vars_to_check = LRUM; - else if (!strcmp(optarg, "LRUS")) - vars_to_check = LRUS; - else if (strncmp(optarg, "VPF", 3) == 0) { - vars_to_check = VPF; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VKF", 3) == 0) { - vars_to_check = VKF; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VMF", 3) == 0) { - vars_to_check = VMF; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (!strcmp(optarg, "DSDB")) - vars_to_check = DSDB; - else if (!strcmp(optarg, "LOGINS")) - vars_to_check = LOGINS; - else if (!strcmp(optarg, "NRMH")) - vars_to_check = NRMH; - else if (!strcmp(optarg, "UPRB")) - vars_to_check = UPRB; - else if (!strcmp(optarg, "PUPRB")) - vars_to_check = PUPRB; - else if (!strncmp(optarg, "SAPENTRIES", 10)) { - vars_to_check = SAPENTRIES; - if (strlen(optarg) > 10) - sap_number = atoi(optarg + 10); - else - sap_number = -1; - } else if (!strcmp(optarg, "OFILES")) - vars_to_check = OFILES; - else if (strncmp(optarg, "VKP", 3) == 0) { - vars_to_check = VKP; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VMP", 3) == 0) { - vars_to_check = VMP; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VMU", 3) == 0) { - vars_to_check = VMU; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VPU", 3) == 0) { - vars_to_check = VPU; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VPP", 3) == 0) { - vars_to_check = VPP; - volume_name = strdup(optarg + 3); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VKNP", 4) == 0) { - vars_to_check = VKNP; - volume_name = strdup(optarg + 4); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (strncmp(optarg, "VPNP", 4) == 0) { - vars_to_check = VPNP; - volume_name = strdup(optarg + 4); - if (!strcmp(volume_name, "")) - volume_name = strdup("SYS"); - } else if (!strcmp(optarg, "ABENDS")) - vars_to_check = ABENDS; - else if (!strcmp(optarg, "CSPROCS")) - vars_to_check = CSPROCS; - else if (!strcmp(optarg, "TSYNC")) - vars_to_check = TSYNC; - else if (!strcmp(optarg, "DSVER")) - vars_to_check = DSVER; - else if (!strcmp(optarg, "UPTIME")) { - vars_to_check = UPTIME; - } else if (strncmp(optarg, "NLM:", 4) == 0) { - vars_to_check = NLM; - nlm_name = strdup(optarg + 4); - } else if (strncmp(optarg, "NRMP", 4) == 0) { - vars_to_check = NRMP; - nrmp_name = strdup(optarg + 4); - if (!strcmp(nrmp_name, "")) - nrmp_name = strdup("AVAILABLE_MEMORY"); - } else if (strncmp(optarg, "NRMM", 4) == 0) { - vars_to_check = NRMM; - nrmm_name = strdup(optarg + 4); - if (!strcmp(nrmm_name, "")) - nrmm_name = strdup("AVAILABLE_CACHE_MEMORY"); - - } - - else if (strncmp(optarg, "NRMS", 4) == 0) { - vars_to_check = NRMS; - nrms_name = strdup(optarg + 4); - if (!strcmp(nrms_name, "")) - nrms_name = strdup("USED_SWAP_SPACE"); - - } - - else if (strncmp(optarg, "NSS1", 4) == 0) { - vars_to_check = NSS1; - nss1_name = strdup(optarg + 4); - if (!strcmp(nss1_name, "")) - nss1_name = strdup("CURRENTBUFFERCACHESIZE"); - - } - - else if (strncmp(optarg, "NSS2", 4) == 0) { - vars_to_check = NSS2; - nss2_name = strdup(optarg + 4); - if (!strcmp(nss2_name, "")) - nss2_name = strdup("CACHEHITS"); - - } - - else if (strncmp(optarg, "NSS3", 4) == 0) { - vars_to_check = NSS3; - nss3_name = strdup(optarg + 4); - if (!strcmp(nss3_name, "")) - nss3_name = strdup("CACHEGITPERCENT"); - - } - - else if (strncmp(optarg, "NSS4", 4) == 0) { - vars_to_check = NSS4; - nss4_name = strdup(optarg + 4); - if (!strcmp(nss4_name, "")) - nss4_name = strdup("CURRENTOPENCOUNT"); - - } - - else if (strncmp(optarg, "NSS5", 4) == 0) { - vars_to_check = NSS5; - nss5_name = strdup(optarg + 4); - if (!strcmp(nss5_name, "")) - nss5_name = strdup("CACHEMISSES"); - - } - - else if (strncmp(optarg, "NSS6", 4) == 0) { - vars_to_check = NSS6; - nss6_name = strdup(optarg + 4); - if (!strcmp(nss6_name, "")) - nss6_name = strdup("PENDINGWORKSCOUNT"); - - } - - else if (strncmp(optarg, "NSS7", 4) == 0) { - vars_to_check = NSS7; - nss7_name = strdup(optarg + 4); - if (!strcmp(nss7_name, "")) - nss7_name = strdup("CACHESIZE"); - - } - - else - return ERROR; - break; - case 'w': /* warning threshold */ - warning_value = strtoul(optarg, NULL, 10); - check_warning_value = true; - break; - case 'c': /* critical threshold */ - critical_value = strtoul(optarg, NULL, 10); - check_critical_value = true; - break; - case 't': /* timeout */ - socket_timeout = atoi(optarg); - if (socket_timeout <= 0) - return ERROR; - } - } - - return OK; -} - -void print_help(void) { - char *myport; - xasprintf(&myport, "%d", PORT); - - print_revision(progname, NP_VERSION); - - printf("Copyright (c) 1999 Ethan Galstad \n"); - printf(COPYRIGHT, copyright, email); - - printf("%s\n", _("This plugin attempts to contact the MRTGEXT NLM running on a")); - printf("%s\n", _("Novell server to gather the requested system information.")); - - printf("\n\n"); - - print_usage(); - - printf(UT_HELP_VRSN); - printf(UT_EXTRA_OPTS); - - printf(UT_HOST_PORT, 'p', myport); - - printf(" %s\n", "-v, --variable=STRING"); - printf(" %s\n", _("Variable to check. Valid variables include:")); - printf(" %s\n", _("LOAD1 = 1 minute average CPU load")); - printf(" %s\n", _("LOAD5 = 5 minute average CPU load")); - printf(" %s\n", _("LOAD15 = 15 minute average CPU load")); - printf(" %s\n", _("CSPROCS = number of current service processes (NW 5.x only)")); - printf(" %s\n", _("ABENDS = number of abended threads (NW 5.x only)")); - printf(" %s\n", _("UPTIME = server uptime")); - printf(" %s\n", _("LTCH = percent long term cache hits")); - printf(" %s\n", _("CBUFF = current number of cache buffers")); - printf(" %s\n", _("CDBUFF = current number of dirty cache buffers")); - printf(" %s\n", _("DCB = dirty cache buffers as a percentage of the total")); - printf(" %s\n", _("TCB = dirty cache buffers as a percentage of the original")); - printf(" %s\n", _("OFILES = number of open files")); - printf(" %s\n", _(" VMF = MB of free space on Volume ")); - printf(" %s\n", _(" VMU = MB used space on Volume ")); - printf(" %s\n", _(" VPU = percent used space on Volume ")); - printf(" %s\n", _(" VMP = MB of purgeable space on Volume ")); - printf(" %s\n", _(" VPF = percent free space on volume ")); - printf(" %s\n", _(" VKF = KB of free space on volume ")); - printf(" %s\n", _(" VPP = percent purgeable space on volume ")); - printf(" %s\n", _(" VKP = KB of purgeable space on volume ")); - printf(" %s\n", _(" VPNP = percent not yet purgeable space on volume ")); - printf(" %s\n", _(" VKNP = KB of not yet purgeable space on volume ")); - printf(" %s\n", _(" LRUM = LRU sitting time in minutes")); - printf(" %s\n", _(" LRUS = LRU sitting time in seconds")); - printf(" %s\n", _(" DSDB = check to see if DS Database is open")); - printf(" %s\n", _(" DSVER = NDS version")); - printf(" %s\n", _(" UPRB = used packet receive buffers")); - printf(" %s\n", _(" PUPRB = percent (of max) used packet receive buffers")); - printf(" %s\n", _(" SAPENTRIES = number of entries in the SAP table")); - printf(" %s\n", _(" SAPENTRIES = number of entries in the SAP table for SAP type ")); - printf(" %s\n", _(" TSYNC = timesync status")); - printf(" %s\n", _(" LOGINS = check to see if logins are enabled")); - printf(" %s\n", _(" CONNS = number of currently licensed connections")); - printf(" %s\n", _(" NRMH = NRM Summary Status")); - printf(" %s\n", _(" NRMP = Returns the current value for a NRM health item")); - printf(" %s\n", _(" NRMM = Returns the current memory stats from NRM")); - printf(" %s\n", _(" NRMS = Returns the current Swapfile stats from NRM")); - printf(" %s\n", _(" NSS1 = Statistics from _Admin:Manage_NSS\\GeneralStats.xml")); - printf(" %s\n", _(" NSS3 = Statistics from _Admin:Manage_NSS\\NameCache.xml")); - printf(" %s\n", _(" NSS4 = Statistics from _Admin:Manage_NSS\\FileStats.xml")); - printf(" %s\n", _(" NSS5 = Statistics from _Admin:Manage_NSS\\ObjectCache.xml")); - printf(" %s\n", _(" NSS6 = Statistics from _Admin:Manage_NSS\\Thread.xml")); - printf(" %s\n", _(" NSS7 = Statistics from _Admin:Manage_NSS\\AuthorizationCache.xml")); - printf(" %s\n", _(" NLM: = check if NLM is loaded and report version")); - printf(" %s\n", _(" (e.g. NLM:TSANDS.NLM)")); - printf("\n"); - printf(" %s\n", "-w, --warning=INTEGER"); - printf(" %s\n", _("Threshold which will result in a warning status")); - printf(" %s\n", "-c, --critical=INTEGER"); - printf(" %s\n", _("Threshold which will result in a critical status")); - printf(" %s\n", "-o, --osversion"); - printf(" %s\n", _("Include server version string in results")); - - printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); - - printf("\n"); - printf("%s\n", _("Notes:")); - printf(" %s\n", _("- This plugin requires that the MRTGEXT.NLM file from James Drews' MRTG")); - printf(" %s\n", _(" extension for NetWare be loaded on the Novell servers you wish to check.")); - printf(" %s\n", _(" (available from http://www.engr.wisc.edu/~drews/mrtg/)")); - printf(" %s\n", _("- Values for critical thresholds should be lower than warning thresholds")); - printf(" %s\n", _(" when the following variables are checked: VPF, VKF, LTCH, CBUFF, DCB, ")); - printf(" %s\n", _(" TCB, LRUS and LRUM.")); - - printf(UT_SUPPORT); -} - -void print_usage(void) { - printf("%s\n", _("Usage:")); - printf("%s -H host [-p port] [-v variable] [-w warning] [-c critical] [-t timeout]\n", progname); -} -- cgit v1.2.3-74-g34f1 From c71a2e414ff3716325a6f4ce715e4a3037d52c0e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 13:54:00 +0100 Subject: check_procs: clang-format --- plugins/check_procs.c | 843 +++++++++++++++++++++++++------------------------- 1 file changed, 422 insertions(+), 421 deletions(-) diff --git a/plugins/check_procs.c b/plugins/check_procs.c index 1d78ccee..69b424dc 100644 --- a/plugins/check_procs.c +++ b/plugins/check_procs.c @@ -1,41 +1,41 @@ /***************************************************************************** -* -* Monitoring check_procs plugin -* -* License: GPL -* Copyright (c) 2000-2024 Monitoring Plugins Development Team -* -* Description: -* -* This file contains the check_procs plugin -* -* Checks all processes and generates WARNING or CRITICAL states if the -* specified metric is outside the required threshold ranges. The metric -* defaults to number of processes. Search filters can be applied to limit -* the processes to check. -* -* The parent process, check_procs itself and any child process of -* check_procs (ps) are excluded from any checks to prevent false positives. -* -* -* 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_procs plugin + * + * License: GPL + * Copyright (c) 2000-2024 Monitoring Plugins Development Team + * + * Description: + * + * This file contains the check_procs plugin + * + * Checks all processes and generates WARNING or CRITICAL states if the + * specified metric is outside the required threshold ranges. The metric + * defaults to number of processes. Search filters can be applied to limit + * the processes to check. + * + * The parent process, check_procs itself and any child process of + * check_procs (ps) are excluded from any checks to prevent false positives. + * + * + * 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 . + * + * + *****************************************************************************/ const char *progname = "check_procs"; -const char *program_name = "check_procs"; /* Required for coreutils libs */ +const char *program_name = "check_procs"; /* Required for coreutils libs */ const char *copyright = "2000-2024"; const char *email = "devel@monitoring-plugins.org"; @@ -48,35 +48,36 @@ const char *email = "devel@monitoring-plugins.org"; #include #ifdef HAVE_SYS_STAT_H -#include +# include #endif -static int process_arguments (int /*argc*/, char ** /*argv*/); -static int validate_arguments (void); -static int convert_to_seconds (char * /*etime*/); -static void print_help (void); -void print_usage (void); +static int process_arguments(int /*argc*/, char ** /*argv*/); +static int validate_arguments(void); +static int convert_to_seconds(char * /*etime*/); +static void print_help(void); +void print_usage(void); static char *warning_range = NULL; static char *critical_range = NULL; static thresholds *procs_thresholds = NULL; static int options = 0; /* bitmask of filter criteria to test against */ -#define ALL 1 -#define STAT 2 -#define PPID 4 -#define USER 8 -#define PROG 16 -#define ARGS 32 -#define VSZ 64 -#define RSS 128 -#define PCPU 256 -#define ELAPSED 512 -#define EREG_ARGS 1024 +#define ALL 1 +#define STAT 2 +#define PPID 4 +#define USER 8 +#define PROG 16 +#define ARGS 32 +#define VSZ 64 +#define RSS 128 +#define PCPU 256 +#define ELAPSED 512 +#define EREG_ARGS 1024 #define EXCLUDE_PROGS 2048 -#define KTHREAD_PARENT "kthreadd" /* the parent process of kernel threads: - ppid of procs are compared to pid of this proc*/ +#define KTHREAD_PARENT \ + "kthreadd" /* the parent process of kernel threads: \ + ppid of procs are compared to pid of this proc*/ /* Different metrics */ char *metric_name; @@ -109,8 +110,7 @@ static char tmp[MAX_INPUT_BUFFER]; static int kthread_filter = 0; static int usepid = 0; /* whether to test for pid or /proc/pid/exe */ -static int -stat_exe (const pid_t pid, struct stat *buf) { +static int stat_exe(const pid_t pid, struct stat *buf) { char *path; int ret; xasprintf(&path, "/proc/%d/exe", pid); @@ -119,10 +119,7 @@ stat_exe (const pid_t pid, struct stat *buf) { return ret; } - -int -main (int argc, char **argv) -{ +int main(int argc, char **argv) { char *input_buffer; char *input_line; char *procprog; @@ -141,16 +138,16 @@ main (int argc, char **argv) int procseconds = 0; float procpcpu = 0; char procstat[8]; - char procetime[MAX_INPUT_BUFFER] = { '\0' }; + char procetime[MAX_INPUT_BUFFER] = {'\0'}; char *procargs; const char *zombie = "Z"; int resultsum = 0; /* bitmask of the filter criteria met by a process */ - int found = 0; /* counter for number of lines returned in `ps` output */ - int procs = 0; /* counter for number of processes meeting filter criteria */ - int pos; /* number of spaces before 'args' in `ps` output */ - int cols; /* number of columns in ps output */ + int found = 0; /* counter for number of lines returned in `ps` output */ + int procs = 0; /* counter for number of processes meeting filter criteria */ + int pos; /* number of spaces before 'args' in `ps` output */ + int cols; /* number of columns in ps output */ int expected_cols = PS_COLS - 1; int warn = 0; /* number of processes in warn state */ int crit = 0; /* number of processes in crit state */ @@ -159,22 +156,23 @@ main (int argc, char **argv) int ret = 0; output chld_out, chld_err; - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); setlocale(LC_NUMERIC, "POSIX"); - input_buffer = malloc (MAX_INPUT_BUFFER); - procprog = malloc (MAX_INPUT_BUFFER); + input_buffer = malloc(MAX_INPUT_BUFFER); + procprog = malloc(MAX_INPUT_BUFFER); - xasprintf (&metric_name, "PROCS"); + xasprintf(&metric_name, "PROCS"); metric = METRIC_PROCS; /* 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")); + if (process_arguments(argc, argv) == ERROR) { + usage4(_("Could not parse arguments")); + } /* find ourself */ mypid = getpid(); @@ -189,44 +187,46 @@ main (int argc, char **argv) } /* Set signal handling and alarm timeout */ - if (signal (SIGALRM, timeout_alarm_handler) == SIG_ERR) { - die (STATE_UNKNOWN, _("Cannot catch SIGALRM")); + if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { + die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); } - (void) alarm ((unsigned) timeout_interval); + (void)alarm((unsigned)timeout_interval); - if (verbose >= 2) - printf (_("CMD: %s\n"), PS_COMMAND); + if (verbose >= 2) { + printf(_("CMD: %s\n"), PS_COMMAND); + } if (input_filename == NULL) { - result = cmd_run( PS_COMMAND, &chld_out, &chld_err, 0); + result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0); if (chld_err.lines > 0) { - printf ("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); + printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); exit(STATE_WARNING); } } else { - result = cmd_file_read( input_filename, &chld_out, 0); + result = cmd_file_read(input_filename, &chld_out, 0); } /* flush first line: j starts at 1 */ for (size_t j = 1; j < chld_out.lines; j++) { input_line = chld_out.line[j]; - if (verbose >= 3) - printf ("%s", input_line); + if (verbose >= 3) { + printf("%s", input_line); + } - strcpy (procprog, ""); - xasprintf (&procargs, "%s", ""); + strcpy(procprog, ""); + xasprintf(&procargs, "%s", ""); - cols = sscanf (input_line, PS_FORMAT, PS_VARLIST); + cols = sscanf(input_line, PS_FORMAT, PS_VARLIST); /* Zombie processes do not give a procprog command */ - if ( cols < expected_cols && strstr(procstat, zombie) ) { + if (cols < expected_cols && strstr(procstat, zombie)) { cols = expected_cols; } - if ( cols >= expected_cols ) { + if (cols >= expected_cols) { resultsum = 0; - xasprintf (&procargs, "%s", input_line + pos); - strip (procargs); + xasprintf(&procargs, "%s", input_line + pos); + strip(procargs); /* Some ps return full pathname for command. This removes path */ strcpy(procprog, base_name(procprog)); @@ -234,52 +234,53 @@ main (int argc, char **argv) /* we need to convert the elapsed time to seconds */ procseconds = convert_to_seconds(procetime); - if (verbose >= 3) - printf ("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", - procs, procuid, procvsz, procrss, - procpid, procppid, procpcpu, procstat, - procetime, procprog, procargs); + if (verbose >= 3) { + printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", procs, procuid, procvsz, + procrss, procpid, procppid, procpcpu, procstat, procetime, procprog, procargs); + } /* Ignore self */ if ((usepid && mypid == procpid) || - ( ((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || - (ret == -1 && errno == ENOENT)) - ) { - if (verbose >= 3) - printf("not considering - is myself or gone\n"); + (((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || + (ret == -1 && errno == ENOENT))) { + if (verbose >= 3) { + printf("not considering - is myself or gone\n"); + } continue; } /* Ignore parent*/ else if (myppid == procpid) { - if (verbose >= 3) - printf("not considering - is parent\n"); + if (verbose >= 3) { + printf("not considering - is parent\n"); + } continue; } /* Ignore our own children */ if (procppid == mypid) { - if (verbose >= 3) - printf("not considering - is our child\n"); + if (verbose >= 3) { + printf("not considering - is our child\n"); + } continue; } /* Ignore excluded processes by name */ - if(options & EXCLUDE_PROGS) { - int found = 0; - int i = 0; - - for(i=0; i < (exclude_progs_counter); i++) { - if(!strcmp(procprog, exclude_progs_arr[i])) { - found = 1; - } - } - if(found == 0) { - resultsum |= EXCLUDE_PROGS; - } else - { - if(verbose >= 3) - printf("excluding - by ignorelist\n"); - } + if (options & EXCLUDE_PROGS) { + int found = 0; + int i = 0; + + for (i = 0; i < (exclude_progs_counter); i++) { + if (!strcmp(procprog, exclude_progs_arr[i])) { + found = 1; + } + } + if (found == 0) { + resultsum |= EXCLUDE_PROGS; + } else { + if (verbose >= 3) { + printf("excluding - by ignorelist\n"); + } + } } /* filter kernel threads (children of KTHREAD_PARENT)*/ @@ -287,69 +288,81 @@ main (int argc, char **argv) sorry for not doing that, but I've no other OSes to test :-( */ if (kthread_filter == 1) { /* get pid KTHREAD_PARENT */ - if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT) ) + if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) { kthread_ppid = procpid; + } if (kthread_ppid == procppid) { - if (verbose >= 2) - printf ("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); + if (verbose >= 2) { + printf("Ignore kernel thread: pid=%d ppid=%d prog=%s args=%s\n", procpid, procppid, procprog, procargs); + } continue; } } - if ((options & STAT) && (strstr (procstat, statopts))) + if ((options & STAT) && (strstr(procstat, statopts))) { resultsum |= STAT; - if ((options & ARGS) && procargs && (strstr (procargs, args) != NULL)) + } + if ((options & ARGS) && procargs && (strstr(procargs, args) != NULL)) { resultsum |= ARGS; - if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t) 0, NULL, 0) == 0)) + } + if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t)0, NULL, 0) == 0)) { resultsum |= EREG_ARGS; - if ((options & PROG) && procprog && (strcmp (prog, procprog) == 0)) + } + if ((options & PROG) && procprog && (strcmp(prog, procprog) == 0)) { resultsum |= PROG; - if ((options & PPID) && (procppid == ppid)) + } + if ((options & PPID) && (procppid == ppid)) { resultsum |= PPID; - if ((options & USER) && (procuid == uid)) + } + if ((options & USER) && (procuid == uid)) { resultsum |= USER; - if ((options & VSZ) && (procvsz >= vsz)) + } + if ((options & VSZ) && (procvsz >= vsz)) { resultsum |= VSZ; - if ((options & RSS) && (procrss >= rss)) + } + if ((options & RSS) && (procrss >= rss)) { resultsum |= RSS; - if ((options & PCPU) && (procpcpu >= pcpu)) + } + if ((options & PCPU) && (procpcpu >= pcpu)) { resultsum |= PCPU; + } found++; /* Next line if filters not matched */ - if (!(options == resultsum || options == ALL)) + if (!(options == resultsum || options == ALL)) { continue; + } procs++; if (verbose >= 2) { - printf ("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", - procuid, procvsz, procrss, - procpid, procppid, procpcpu, procstat, - procetime, procprog, procargs); + printf("Matched: uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", procuid, procvsz, + procrss, procpid, procppid, procpcpu, procstat, procetime, procprog, procargs); } - if (metric == METRIC_VSZ) - i = get_status ((double)procvsz, procs_thresholds); - else if (metric == METRIC_RSS) - i = get_status ((double)procrss, procs_thresholds); + if (metric == METRIC_VSZ) { + i = get_status((double)procvsz, procs_thresholds); + } else if (metric == METRIC_RSS) { + i = get_status((double)procrss, procs_thresholds); + } /* TODO? float thresholds for --metric=CPU */ - else if (metric == METRIC_CPU) - i = get_status (procpcpu, procs_thresholds); - else if (metric == METRIC_ELAPSED) - i = get_status ((double)procseconds, procs_thresholds); + else if (metric == METRIC_CPU) { + i = get_status(procpcpu, procs_thresholds); + } else if (metric == METRIC_ELAPSED) { + i = get_status((double)procseconds, procs_thresholds); + } if (metric != METRIC_PROCS) { if (i == STATE_WARNING) { warn++; - xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); - result = max_state (result, i); + xasprintf(&fails, "%s%s%s", fails, (strcmp(fails, "") ? ", " : ""), procprog); + result = max_state(result, i); } if (i == STATE_CRITICAL) { crit++; - xasprintf (&fails, "%s%s%s", fails, (strcmp(fails,"") ? ", " : ""), procprog); - result = max_state (result, i); + xasprintf(&fails, "%s%s%s", fails, (strcmp(fails, "") ? ", " : ""), procprog); + result = max_state(result, i); } } } @@ -359,58 +372,55 @@ main (int argc, char **argv) } } - if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ - printf (_("Unable to read output\n")); + if (found == 0) { /* no process lines parsed so return STATE_UNKNOWN */ + printf(_("Unable to read output\n")); return STATE_UNKNOWN; } - if ( result == STATE_UNKNOWN ) + if (result == STATE_UNKNOWN) { result = STATE_OK; + } /* Needed if procs found, but none match filter */ - if ( metric == METRIC_PROCS ) { - result = max_state (result, get_status ((double)procs, procs_thresholds) ); + if (metric == METRIC_PROCS) { + result = max_state(result, get_status((double)procs, procs_thresholds)); } - if ( result == STATE_OK ) { - printf ("%s %s: ", metric_name, _("OK")); + if (result == STATE_OK) { + printf("%s %s: ", metric_name, _("OK")); } else if (result == STATE_WARNING) { - printf ("%s %s: ", metric_name, _("WARNING")); - if ( metric != METRIC_PROCS ) { - printf (_("%d warn out of "), warn); + printf("%s %s: ", metric_name, _("WARNING")); + if (metric != METRIC_PROCS) { + printf(_("%d warn out of "), warn); } } else if (result == STATE_CRITICAL) { - printf ("%s %s: ", metric_name, _("CRITICAL")); + printf("%s %s: ", metric_name, _("CRITICAL")); if (metric != METRIC_PROCS) { - printf (_("%d crit, %d warn out of "), crit, warn); + printf(_("%d crit, %d warn out of "), crit, warn); } } - printf (ngettext ("%d process", "%d processes", (unsigned long) procs), procs); + printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs); - if (strcmp(fmt,"") != 0) { - printf (_(" with %s"), fmt); + if (strcmp(fmt, "") != 0) { + printf(_(" with %s"), fmt); } - if ( verbose >= 1 && strcmp(fails,"") ) - printf (" [%s]", fails); + if (verbose >= 1 && strcmp(fails, "")) { + printf(" [%s]", fails); + } - if (metric == METRIC_PROCS) - printf (" | procs=%d;%s;%s;0;", procs, - warning_range ? warning_range : "", - critical_range ? critical_range : ""); - else - printf (" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); + if (metric == METRIC_PROCS) { + printf(" | procs=%d;%s;%s;0;", procs, warning_range ? warning_range : "", critical_range ? critical_range : ""); + } else { + printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); + } - printf ("\n"); + printf("\n"); return result; } - - /* process command-line arguments */ -int -process_arguments (int argc, char **argv) -{ +int process_arguments(int argc, char **argv) { int c = 1; char *user; struct passwd *pw; @@ -419,260 +429,262 @@ process_arguments (int argc, char **argv) int cflags = REG_NOSUB | REG_EXTENDED; char errbuf[MAX_INPUT_BUFFER]; char *temp_string; - int i=0; - static struct option longopts[] = { - {"warning", required_argument, 0, 'w'}, - {"critical", required_argument, 0, 'c'}, - {"metric", required_argument, 0, 'm'}, - {"timeout", required_argument, 0, 't'}, - {"status", required_argument, 0, 's'}, - {"ppid", required_argument, 0, 'p'}, - {"user", required_argument, 0, 'u'}, - {"command", required_argument, 0, 'C'}, - {"vsz", required_argument, 0, 'z'}, - {"rss", required_argument, 0, 'r'}, - {"pcpu", required_argument, 0, 'P'}, - {"elapsed", required_argument, 0, 'e'}, - {"argument-array", required_argument, 0, 'a'}, - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"verbose", no_argument, 0, 'v'}, - {"ereg-argument-array", required_argument, 0, CHAR_MAX+1}, - {"input-file", required_argument, 0, CHAR_MAX+2}, - {"no-kthreads", required_argument, 0, 'k'}, - {"traditional-filter", no_argument, 0, 'T'}, - {"exclude-process", required_argument, 0, 'X'}, - {0, 0, 0, 0} - }; - - for (c = 1; c < argc; c++) - if (strcmp ("-to", argv[c]) == 0) - strcpy (argv[c], "-t"); + int i = 0; + static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, + {"metric", required_argument, 0, 'm'}, + {"timeout", required_argument, 0, 't'}, + {"status", required_argument, 0, 's'}, + {"ppid", required_argument, 0, 'p'}, + {"user", required_argument, 0, 'u'}, + {"command", required_argument, 0, 'C'}, + {"vsz", required_argument, 0, 'z'}, + {"rss", required_argument, 0, 'r'}, + {"pcpu", required_argument, 0, 'P'}, + {"elapsed", required_argument, 0, 'e'}, + {"argument-array", required_argument, 0, 'a'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"verbose", no_argument, 0, 'v'}, + {"ereg-argument-array", required_argument, 0, CHAR_MAX + 1}, + {"input-file", required_argument, 0, CHAR_MAX + 2}, + {"no-kthreads", required_argument, 0, 'k'}, + {"traditional-filter", no_argument, 0, 'T'}, + {"exclude-process", required_argument, 0, 'X'}, + {0, 0, 0, 0}}; + + for (c = 1; c < argc; c++) { + if (strcmp("-to", argv[c]) == 0) { + strcpy(argv[c], "-t"); + } + } while (1) { - c = getopt_long (argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", - longopts, &option); + c = getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option); - if (c == -1 || c == EOF) + if (c == -1 || c == EOF) { break; + } switch (c) { - case '?': /* help */ - usage5 (); - case 'h': /* help */ - print_help (); - exit (STATE_UNKNOWN); - case 'V': /* version */ - print_revision (progname, NP_VERSION); - exit (STATE_UNKNOWN); - case 't': /* timeout period */ - if (!is_integer (optarg)) - usage2 (_("Timeout interval must be a positive integer"), optarg); - else - timeout_interval = atoi (optarg); + case '?': /* help */ + usage5(); + case 'h': /* help */ + print_help(); + exit(STATE_UNKNOWN); + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + case 't': /* timeout period */ + if (!is_integer(optarg)) { + usage2(_("Timeout interval must be a positive integer"), optarg); + } else { + timeout_interval = atoi(optarg); + } break; - case 'c': /* critical threshold */ + case 'c': /* critical threshold */ critical_range = optarg; break; - case 'w': /* warning threshold */ + case 'w': /* warning threshold */ warning_range = optarg; break; - case 'p': /* process id */ - if (sscanf (optarg, "%d%[^0-9]", &ppid, tmp) == 1) { - xasprintf (&fmt, "%s%sPPID = %d", (fmt ? fmt : "") , (options ? ", " : ""), ppid); + case 'p': /* process id */ + if (sscanf(optarg, "%d%[^0-9]", &ppid, tmp) == 1) { + xasprintf(&fmt, "%s%sPPID = %d", (fmt ? fmt : ""), (options ? ", " : ""), ppid); options |= PPID; break; } - usage4 (_("Parent Process ID must be an integer!")); - case 's': /* status */ - if (statopts) + usage4(_("Parent Process ID must be an integer!")); + case 's': /* status */ + if (statopts) { break; - else + } else { statopts = optarg; - xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); + } + xasprintf(&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); options |= STAT; break; - case 'u': /* user or user id */ - if (is_integer (optarg)) { - uid = atoi (optarg); - pw = getpwuid ((uid_t) uid); + case 'u': /* user or user id */ + if (is_integer(optarg)) { + uid = atoi(optarg); + pw = getpwuid((uid_t)uid); /* check to be sure user exists */ - if (pw == NULL) - usage2 (_("UID was not found"), optarg); - } - else { - pw = getpwnam (optarg); + if (pw == NULL) { + usage2(_("UID was not found"), optarg); + } + } else { + pw = getpwnam(optarg); /* check to be sure user exists */ - if (pw == NULL) - usage2 (_("User name was not found"), optarg); + if (pw == NULL) { + usage2(_("User name was not found"), optarg); + } /* then get uid */ uid = pw->pw_uid; } user = pw->pw_name; - xasprintf (&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), - uid, user); + xasprintf(&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), uid, user); options |= USER; break; - case 'C': /* command */ + case 'C': /* command */ /* TODO: allow this to be passed in with --metric */ - if (prog) + if (prog) { break; - else + } else { prog = optarg; - xasprintf (&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), - prog); + } + xasprintf(&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), prog); options |= PROG; break; case 'X': - if(exclude_progs) - break; - else - exclude_progs = optarg; - xasprintf (&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), - exclude_progs); + if (exclude_progs) { + break; + } else { + exclude_progs = optarg; + } + xasprintf(&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), exclude_progs); char *p = strtok(exclude_progs, ","); - while(p){ - exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char*) * ++exclude_progs_counter); - exclude_progs_arr[exclude_progs_counter-1] = p; - p = strtok(NULL, ","); + while (p) { + exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char *) * ++exclude_progs_counter); + exclude_progs_arr[exclude_progs_counter - 1] = p; + p = strtok(NULL, ","); } options |= EXCLUDE_PROGS; break; - case 'a': /* args (full path name with args) */ + case 'a': /* args (full path name with args) */ /* TODO: allow this to be passed in with --metric */ - if (args) + if (args) { break; - else + } else { args = optarg; - xasprintf (&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); + } + xasprintf(&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); options |= ARGS; break; - case CHAR_MAX+1: + case CHAR_MAX + 1: err = regcomp(&re_args, optarg, cflags); if (err != 0) { - regerror (err, &re_args, errbuf, MAX_INPUT_BUFFER); - die (STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); + regerror(err, &re_args, errbuf, MAX_INPUT_BUFFER); + die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } /* Strip off any | within the regex optarg */ temp_string = strdup(optarg); - while(temp_string[i]!='\0'){ - if(temp_string[i]=='|') - temp_string[i]=','; + while (temp_string[i] != '\0') { + if (temp_string[i] == '|') { + temp_string[i] = ','; + } i++; } - xasprintf (&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); + xasprintf(&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); options |= EREG_ARGS; break; - case 'r': /* RSS */ - if (sscanf (optarg, "%d%[^0-9]", &rss, tmp) == 1) { - xasprintf (&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); + case 'r': /* RSS */ + if (sscanf(optarg, "%d%[^0-9]", &rss, tmp) == 1) { + xasprintf(&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); options |= RSS; break; } - usage4 (_("RSS must be an integer!")); - case 'z': /* VSZ */ - if (sscanf (optarg, "%d%[^0-9]", &vsz, tmp) == 1) { - xasprintf (&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); + usage4(_("RSS must be an integer!")); + case 'z': /* VSZ */ + if (sscanf(optarg, "%d%[^0-9]", &vsz, tmp) == 1) { + xasprintf(&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); options |= VSZ; break; } - usage4 (_("VSZ must be an integer!")); - case 'P': /* PCPU */ + usage4(_("VSZ must be an integer!")); + case 'P': /* PCPU */ /* TODO: -P 1.5.5 is accepted */ - if (sscanf (optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { - xasprintf (&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); + if (sscanf(optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { + xasprintf(&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); options |= PCPU; break; } - usage4 (_("PCPU must be a float!")); + usage4(_("PCPU must be a float!")); case 'm': - xasprintf (&metric_name, "%s", optarg); - if ( strcmp(optarg, "PROCS") == 0) { + xasprintf(&metric_name, "%s", optarg); + if (strcmp(optarg, "PROCS") == 0) { metric = METRIC_PROCS; break; - } - else if ( strcmp(optarg, "VSZ") == 0) { + } else if (strcmp(optarg, "VSZ") == 0) { metric = METRIC_VSZ; break; - } - else if ( strcmp(optarg, "RSS") == 0 ) { + } else if (strcmp(optarg, "RSS") == 0) { metric = METRIC_RSS; break; - } - else if ( strcmp(optarg, "CPU") == 0 ) { + } else if (strcmp(optarg, "CPU") == 0) { metric = METRIC_CPU; break; - } - else if ( strcmp(optarg, "ELAPSED") == 0) { + } else if (strcmp(optarg, "ELAPSED") == 0) { metric = METRIC_ELAPSED; break; } - usage4 (_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); - case 'k': /* linux kernel thread filter */ + usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); + case 'k': /* linux kernel thread filter */ kthread_filter = 1; break; - case 'v': /* command */ + case 'v': /* command */ verbose++; break; case 'T': usepid = 1; break; - case CHAR_MAX+2: + case CHAR_MAX + 2: input_filename = optarg; break; } } c = optind; - if ((! warning_range) && argv[c]) + if ((!warning_range) && argv[c]) { warning_range = argv[c++]; - if ((! critical_range) && argv[c]) + } + if ((!critical_range) && argv[c]) { critical_range = argv[c++]; + } if (statopts == NULL && argv[c]) { - xasprintf (&statopts, "%s", argv[c++]); - xasprintf (&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); + xasprintf(&statopts, "%s", argv[c++]); + xasprintf(&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); options |= STAT; } /* this will abort in case of invalid ranges */ - set_thresholds (&procs_thresholds, warning_range, critical_range); + set_thresholds(&procs_thresholds, warning_range, critical_range); - return validate_arguments (); + return validate_arguments(); } - - -int -validate_arguments () -{ - if (options == 0) +int validate_arguments() { + if (options == 0) { options = ALL; + } - if (statopts==NULL) + if (statopts == NULL) { statopts = strdup(""); + } - if (prog==NULL) + if (prog == NULL) { prog = strdup(""); + } - if (args==NULL) + if (args == NULL) { args = strdup(""); + } - if (fmt==NULL) + if (fmt == NULL) { fmt = strdup(""); + } - if (fails==NULL) + if (fails == NULL) { fails = strdup(""); + } return options; } - /* convert the elapsed time to seconds */ -int -convert_to_seconds(char *etime) { +int convert_to_seconds(char *etime) { char *ptr; int total; @@ -704,8 +716,7 @@ convert_to_seconds(char *etime) { } if (hyphcnt > 0) { - sscanf(etime, "%d-%d:%d:%d", - &days, &hours, &minutes, &seconds); + sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds); /* linux 2.6.5/2.6.6 reporting some processes with infinite * elapsed times for some reason */ if (days == 49710) { @@ -713,135 +724,125 @@ convert_to_seconds(char *etime) { } } else { if (coloncnt == 2) { - sscanf(etime, "%d:%d:%d", - &hours, &minutes, &seconds); + sscanf(etime, "%d:%d:%d", &hours, &minutes, &seconds); } else if (coloncnt == 1) { - sscanf(etime, "%d:%d", - &minutes, &seconds); + sscanf(etime, "%d:%d", &minutes, &seconds); } } - total = (days * 86400) + - (hours * 3600) + - (minutes * 60) + - seconds; + total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds; if (verbose >= 3 && metric == METRIC_ELAPSED) { - printf("seconds: %d\n", total); + printf("seconds: %d\n", total); } return total; } +void print_help(void) { + print_revision(progname, NP_VERSION); -void -print_help (void) -{ - print_revision (progname, NP_VERSION); - - printf ("Copyright (c) 1999 Ethan Galstad \n"); - printf (COPYRIGHT, copyright, email); + printf("Copyright (c) 1999 Ethan Galstad \n"); + printf(COPYRIGHT, copyright, email); - printf ("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); - printf ("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); - printf ("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); + printf("%s\n", _("Checks all processes and generates WARNING or CRITICAL states if the specified")); + printf("%s\n", _("metric is outside the required threshold ranges. The metric defaults to number")); + printf("%s\n", _("of processes. Search filters can be applied to limit the processes to check.")); - printf ("\n\n"); + printf("\n\n"); - printf ("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); - printf ("%s\n", _("are excluded from any checks to prevent false positives.")); + printf("%s\n", _("The parent process, check_procs itself and any child process of check_procs (ps)")); + printf("%s\n", _("are excluded from any checks to prevent false positives.")); - printf ("\n\n"); + printf("\n\n"); - print_usage (); + print_usage(); - printf (UT_HELP_VRSN); - printf (UT_EXTRA_OPTS); - printf (" %s\n", "-w, --warning=RANGE"); - printf (" %s\n", _("Generate warning state if metric is outside this range")); - printf (" %s\n", "-c, --critical=RANGE"); - printf (" %s\n", _("Generate critical state if metric is outside this range")); - printf (" %s\n", "-m, --metric=TYPE"); - printf (" %s\n", _("Check thresholds against metric. Valid types:")); - printf (" %s\n", _("PROCS - number of processes (default)")); - printf (" %s\n", _("VSZ - virtual memory size")); - printf (" %s\n", _("RSS - resident set memory size")); - printf (" %s\n", _("CPU - percentage CPU")); + printf(UT_HELP_VRSN); + printf(UT_EXTRA_OPTS); + printf(" %s\n", "-w, --warning=RANGE"); + printf(" %s\n", _("Generate warning state if metric is outside this range")); + printf(" %s\n", "-c, --critical=RANGE"); + printf(" %s\n", _("Generate critical state if metric is outside this range")); + printf(" %s\n", "-m, --metric=TYPE"); + printf(" %s\n", _("Check thresholds against metric. Valid types:")); + printf(" %s\n", _("PROCS - number of processes (default)")); + printf(" %s\n", _("VSZ - virtual memory size")); + printf(" %s\n", _("RSS - resident set memory size")); + printf(" %s\n", _("CPU - percentage CPU")); /* only linux etime is support currently */ -#if defined( __linux__ ) - printf (" %s\n", _("ELAPSED - time elapsed in seconds")); +#if defined(__linux__) + printf(" %s\n", _("ELAPSED - time elapsed in seconds")); #endif /* defined(__linux__) */ - printf (UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); - - printf (" %s\n", "-v, --verbose"); - printf (" %s\n", _("Extra information. Up to 3 verbosity levels")); - - printf (" %s\n", "-T, --traditional"); - printf (" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); - - printf ("\n"); - printf ("%s\n", "Filters:"); - printf (" %s\n", "-s, --state=STATUSFLAGS"); - printf (" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); - printf (" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); - printf (" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); - printf (" %s\n", "-p, --ppid=PPID"); - printf (" %s\n", _("Only scan for children of the parent process ID indicated.")); - printf (" %s\n", "-z, --vsz=VSZ"); - printf (" %s\n", _("Only scan for processes with VSZ higher than indicated.")); - printf (" %s\n", "-r, --rss=RSS"); - printf (" %s\n", _("Only scan for processes with RSS higher than indicated.")); - printf (" %s\n", "-P, --pcpu=PCPU"); - printf (" %s\n", _("Only scan for processes with PCPU higher than indicated.")); - printf (" %s\n", "-u, --user=USER"); - printf (" %s\n", _("Only scan for processes with user name or ID indicated.")); - printf (" %s\n", "-a, --argument-array=STRING"); - printf (" %s\n", _("Only scan for processes with args that contain STRING.")); - printf (" %s\n", "--ereg-argument-array=STRING"); - printf (" %s\n", _("Only scan for processes with args that contain the regex STRING.")); - printf (" %s\n", "-C, --command=COMMAND"); - printf (" %s\n", _("Only scan for exact matches of COMMAND (without path).")); - printf (" %s\n", "-X, --exclude-process"); - printf (" %s\n", _("Exclude processes which match this comma separated list")); - printf (" %s\n", "-k, --no-kthreads"); - printf (" %s\n", _("Only scan for non kernel threads (works on Linux only).")); - - printf(_("\n\ + printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + + printf(" %s\n", "-v, --verbose"); + printf(" %s\n", _("Extra information. Up to 3 verbosity levels")); + + printf(" %s\n", "-T, --traditional"); + printf(" %s\n", _("Filter own process the traditional way by PID instead of /proc/pid/exe")); + + printf("\n"); + printf("%s\n", "Filters:"); + printf(" %s\n", "-s, --state=STATUSFLAGS"); + printf(" %s\n", _("Only scan for processes that have, in the output of `ps`, one or")); + printf(" %s\n", _("more of the status flags you specify (for example R, Z, S, RS,")); + printf(" %s\n", _("RSZDT, plus others based on the output of your 'ps' command).")); + printf(" %s\n", "-p, --ppid=PPID"); + printf(" %s\n", _("Only scan for children of the parent process ID indicated.")); + printf(" %s\n", "-z, --vsz=VSZ"); + printf(" %s\n", _("Only scan for processes with VSZ higher than indicated.")); + printf(" %s\n", "-r, --rss=RSS"); + printf(" %s\n", _("Only scan for processes with RSS higher than indicated.")); + printf(" %s\n", "-P, --pcpu=PCPU"); + printf(" %s\n", _("Only scan for processes with PCPU higher than indicated.")); + printf(" %s\n", "-u, --user=USER"); + printf(" %s\n", _("Only scan for processes with user name or ID indicated.")); + printf(" %s\n", "-a, --argument-array=STRING"); + printf(" %s\n", _("Only scan for processes with args that contain STRING.")); + printf(" %s\n", "--ereg-argument-array=STRING"); + printf(" %s\n", _("Only scan for processes with args that contain the regex STRING.")); + printf(" %s\n", "-C, --command=COMMAND"); + printf(" %s\n", _("Only scan for exact matches of COMMAND (without path).")); + printf(" %s\n", "-X, --exclude-process"); + printf(" %s\n", _("Exclude processes which match this comma separated list")); + printf(" %s\n", "-k, --no-kthreads"); + printf(" %s\n", _("Only scan for non kernel threads (works on Linux only).")); + + printf(_("\n\ RANGEs are specified 'min:max' or 'min:' or ':max' (or 'max'). If\n\ specified 'max:min', a warning status will be generated if the\n\ count is inside the specified range\n\n")); - printf(_("\ + printf(_("\ This plugin checks the number of currently running processes and\n\ generates WARNING or CRITICAL states if the process count is outside\n\ the specified threshold ranges. The process count can be filtered by\n\ process owner, parent process PID, current state (e.g., 'Z'), or may\n\ be the total number of running processes\n\n")); - printf ("%s\n", _("Examples:")); - printf (" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); - printf (" %s\n", _("Warning if not two processes with command name portsentry.")); - printf (" %s\n\n", _("Critical if < 2 or > 1024 processes")); - printf (" %s\n", "check_procs -c 1: -C sshd"); - printf (" %s\n", _("Critical if not at least 1 process with command sshd")); - printf (" %s\n", "check_procs -w 1024 -c 1: -C sshd"); - printf (" %s\n", _("Warning if > 1024 processes with command name sshd.")); - printf (" %s\n\n", _("Critical if < 1 processes with command name sshd.")); - printf (" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); - printf (" %s\n", _("Warning alert if > 10 processes with command arguments containing")); - printf (" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); - printf (" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); - printf (" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); - printf (" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); - printf (" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); - - printf (UT_SUPPORT); + printf("%s\n", _("Examples:")); + printf(" %s\n", "check_procs -w 2:2 -c 2:1024 -C portsentry"); + printf(" %s\n", _("Warning if not two processes with command name portsentry.")); + printf(" %s\n\n", _("Critical if < 2 or > 1024 processes")); + printf(" %s\n", "check_procs -c 1: -C sshd"); + printf(" %s\n", _("Critical if not at least 1 process with command sshd")); + printf(" %s\n", "check_procs -w 1024 -c 1: -C sshd"); + printf(" %s\n", _("Warning if > 1024 processes with command name sshd.")); + printf(" %s\n\n", _("Critical if < 1 processes with command name sshd.")); + printf(" %s\n", "check_procs -w 10 -a '/usr/local/bin/perl' -u root"); + printf(" %s\n", _("Warning alert if > 10 processes with command arguments containing")); + printf(" %s\n\n", _("'/usr/local/bin/perl' and owned by root")); + printf(" %s\n", "check_procs -w 50000 -c 100000 --metric=VSZ"); + printf(" %s\n\n", _("Alert if VSZ of any processes over 50K or 100K")); + printf(" %s\n", "check_procs -w 10 -c 20 --metric=CPU"); + printf(" %s\n", _("Alert if CPU of any processes over 10%% or 20%%")); + + printf(UT_SUPPORT); } -void -print_usage (void) -{ - printf ("%s\n", _("Usage:")); - printf ("%s -w -c [-m metric] [-s state] [-p ppid]\n", progname); - printf (" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n"); - printf (" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n"); +void print_usage(void) { + printf("%s\n", _("Usage:")); + printf("%s -w -c [-m metric] [-s state] [-p ppid]\n", progname); + printf(" [-u user] [-r rss] [-z vsz] [-P %%cpu] [-a argument-array]\n"); + printf(" [-C command] [-X process_to_exclude] [-k] [-t timeout] [-v]\n"); } -- cgit v1.2.3-74-g34f1 From 16c3e2499309af654bcf92a4cc516521122e8d66 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 14:53:54 +0100 Subject: check_procs: general refactoring --- configure.ac | 2 +- plugins/check_procs.c | 247 +++++++++++++++++++++++--------------------------- 2 files changed, 114 insertions(+), 135 deletions(-) diff --git a/configure.ac b/configure.ac index 204fc6e3..c66f9b2f 100644 --- a/configure.ac +++ b/configure.ac @@ -796,7 +796,7 @@ elif ps axwo 'stat comm vsz rss user uid pid ppid etime args' 2>/dev/null | \ then ac_cv_ps_varlist="[procstat,&procuid,&procpid,&procppid,&procvsz,&procrss,&procpcpu,procetime,procprog,&pos]" ac_cv_ps_command="$PATH_TO_PS axwo 'stat uid pid ppid vsz rss pcpu etime comm args'" - ac_cv_ps_format="%s %d %d %d %d %d %f %s %s %n" + ac_cv_ps_format="%s %u %d %d %d %d %f %s %s %n" ac_cv_ps_cols=10 AC_MSG_RESULT([$ac_cv_ps_command]) diff --git a/plugins/check_procs.c b/plugins/check_procs.c index 69b424dc..da2198a7 100644 --- a/plugins/check_procs.c +++ b/plugins/check_procs.c @@ -43,6 +43,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "utils.h" #include "utils_cmd.h" #include "regex.h" +#include "states.h" #include #include @@ -91,7 +92,7 @@ enum metric { enum metric metric = METRIC_PROCS; static int verbose = 0; -static int uid; +static uid_t uid; static pid_t ppid; static int vsz; static int rss; @@ -107,62 +108,22 @@ static regex_t re_args; static char *fmt; static char *fails; static char tmp[MAX_INPUT_BUFFER]; -static int kthread_filter = 0; -static int usepid = 0; /* whether to test for pid or /proc/pid/exe */ +static bool kthread_filter = false; +static bool usepid = false; /* whether to test for pid or /proc/pid/exe */ static int stat_exe(const pid_t pid, struct stat *buf) { char *path; - int ret; xasprintf(&path, "/proc/%d/exe", pid); - ret = stat(path, buf); + int ret = stat(path, buf); free(path); return ret; } int main(int argc, char **argv) { - char *input_buffer; - char *input_line; - char *procprog; - - pid_t mypid = 0; - pid_t myppid = 0; - struct stat statbuf; - dev_t mydev = 0; - ino_t myino = 0; - int procuid = 0; - pid_t procpid = 0; - pid_t procppid = 0; - pid_t kthread_ppid = 0; - int procvsz = 0; - int procrss = 0; - int procseconds = 0; - float procpcpu = 0; - char procstat[8]; - char procetime[MAX_INPUT_BUFFER] = {'\0'}; - char *procargs; - - const char *zombie = "Z"; - - int resultsum = 0; /* bitmask of the filter criteria met by a process */ - int found = 0; /* counter for number of lines returned in `ps` output */ - int procs = 0; /* counter for number of processes meeting filter criteria */ - int pos; /* number of spaces before 'args' in `ps` output */ - int cols; /* number of columns in ps output */ - int expected_cols = PS_COLS - 1; - int warn = 0; /* number of processes in warn state */ - int crit = 0; /* number of processes in crit state */ - int i = 0; - int result = STATE_UNKNOWN; - int ret = 0; - output chld_out, chld_err; - setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "POSIX"); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - setlocale(LC_NUMERIC, "POSIX"); - - input_buffer = malloc(MAX_INPUT_BUFFER); - procprog = malloc(MAX_INPUT_BUFFER); xasprintf(&metric_name, "PROCS"); metric = METRIC_PROCS; @@ -175,13 +136,16 @@ int main(int argc, char **argv) { } /* find ourself */ - mypid = getpid(); - myppid = getppid(); + pid_t mypid = getpid(); + pid_t myppid = getppid(); + dev_t mydev = 0; + ino_t myino = 0; + struct stat statbuf; if (usepid || stat_exe(mypid, &statbuf) == -1) { /* usepid might have been set by -T */ - usepid = 1; + usepid = true; } else { - usepid = 0; + usepid = false; mydev = statbuf.st_dev; myino = statbuf.st_ino; } @@ -190,12 +154,15 @@ int main(int argc, char **argv) { if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { die(STATE_UNKNOWN, _("Cannot catch SIGALRM")); } - (void)alarm((unsigned)timeout_interval); + (void)alarm(timeout_interval); if (verbose >= 2) { printf(_("CMD: %s\n"), PS_COMMAND); } + output chld_out; + output chld_err; + mp_state_enum result = STATE_UNKNOWN; if (input_filename == NULL) { result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0); if (chld_err.lines > 0) { @@ -206,20 +173,43 @@ int main(int argc, char **argv) { result = cmd_file_read(input_filename, &chld_out, 0); } + int pos; /* number of spaces before 'args' in `ps` output */ + uid_t procuid = 0; + pid_t procpid = 0; + pid_t procppid = 0; + pid_t kthread_ppid = 0; + int warn = 0; /* number of processes in warn state */ + int crit = 0; /* number of processes in crit state */ + int procvsz = 0; + int procrss = 0; + int procseconds = 0; + float procpcpu = 0; + char procstat[8]; + char procetime[MAX_INPUT_BUFFER] = {'\0'}; + int resultsum = 0; /* bitmask of the filter criteria met by a process */ + int found = 0; /* counter for number of lines returned in `ps` output */ + int procs = 0; /* counter for number of processes meeting filter criteria */ + char *input_buffer = malloc(MAX_INPUT_BUFFER); + char *procprog = malloc(MAX_INPUT_BUFFER); + const int expected_cols = PS_COLS - 1; + /* flush first line: j starts at 1 */ for (size_t j = 1; j < chld_out.lines; j++) { - input_line = chld_out.line[j]; + char *input_line = chld_out.line[j]; if (verbose >= 3) { printf("%s", input_line); } strcpy(procprog, ""); + char *procargs; xasprintf(&procargs, "%s", ""); - cols = sscanf(input_line, PS_FORMAT, PS_VARLIST); + /* number of columns in ps output */ + int cols = sscanf(input_line, PS_FORMAT, PS_VARLIST); /* Zombie processes do not give a procprog command */ + const char *zombie = "Z"; if (cols < expected_cols && strstr(procstat, zombie)) { cols = expected_cols; } @@ -240,6 +230,7 @@ int main(int argc, char **argv) { } /* Ignore self */ + int ret = 0; if ((usepid && mypid == procpid) || (((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || (ret == -1 && errno == ENOENT))) { @@ -249,7 +240,7 @@ int main(int argc, char **argv) { continue; } /* Ignore parent*/ - else if (myppid == procpid) { + if (myppid == procpid) { if (verbose >= 3) { printf("not considering - is parent\n"); } @@ -266,15 +257,13 @@ int main(int argc, char **argv) { /* Ignore excluded processes by name */ if (options & EXCLUDE_PROGS) { - int found = 0; - int i = 0; - - for (i = 0; i < (exclude_progs_counter); i++) { + bool found = false; + for (int i = 0; i < (exclude_progs_counter); i++) { if (!strcmp(procprog, exclude_progs_arr[i])) { - found = 1; + found = true; } } - if (found == 0) { + if (!found) { resultsum |= EXCLUDE_PROGS; } else { if (verbose >= 3) { @@ -286,7 +275,7 @@ int main(int argc, char **argv) { /* filter kernel threads (children of KTHREAD_PARENT)*/ /* TODO adapt for other OSes than GNU/Linux sorry for not doing that, but I've no other OSes to test :-( */ - if (kthread_filter == 1) { + if (kthread_filter) { /* get pid KTHREAD_PARENT */ if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) { kthread_ppid = procpid; @@ -341,28 +330,29 @@ int main(int argc, char **argv) { procrss, procpid, procppid, procpcpu, procstat, procetime, procprog, procargs); } + mp_state_enum temporary_result = STATE_OK; if (metric == METRIC_VSZ) { - i = get_status((double)procvsz, procs_thresholds); + temporary_result = get_status((double)procvsz, procs_thresholds); } else if (metric == METRIC_RSS) { - i = get_status((double)procrss, procs_thresholds); + temporary_result = get_status((double)procrss, procs_thresholds); } /* TODO? float thresholds for --metric=CPU */ else if (metric == METRIC_CPU) { - i = get_status(procpcpu, procs_thresholds); + temporary_result = get_status(procpcpu, procs_thresholds); } else if (metric == METRIC_ELAPSED) { - i = get_status((double)procseconds, procs_thresholds); + temporary_result = get_status((double)procseconds, procs_thresholds); } if (metric != METRIC_PROCS) { - if (i == STATE_WARNING) { + if (temporary_result == STATE_WARNING) { warn++; xasprintf(&fails, "%s%s%s", fails, (strcmp(fails, "") ? ", " : ""), procprog); - result = max_state(result, i); + result = max_state(result, temporary_result); } - if (i == STATE_CRITICAL) { + if (temporary_result == STATE_CRITICAL) { crit++; xasprintf(&fails, "%s%s%s", fails, (strcmp(fails, "") ? ", " : ""), procprog); - result = max_state(result, i); + result = max_state(result, temporary_result); } } } @@ -416,20 +406,11 @@ int main(int argc, char **argv) { } printf("\n"); - return result; + exit(result); } /* process command-line arguments */ int process_arguments(int argc, char **argv) { - int c = 1; - char *user; - struct passwd *pw; - int option = 0; - int err; - int cflags = REG_NOSUB | REG_EXTENDED; - char errbuf[MAX_INPUT_BUFFER]; - char *temp_string; - int i = 0; static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"metric", required_argument, 0, 'm'}, @@ -453,20 +434,21 @@ int process_arguments(int argc, char **argv) { {"exclude-process", required_argument, 0, 'X'}, {0, 0, 0, 0}}; - for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) { - strcpy(argv[c], "-t"); + for (int index = 1; index < argc; index++) { + if (strcmp("-to", argv[index]) == 0) { + strcpy(argv[index], "-t"); } } - while (1) { - c = getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option); + while (true) { + int option = 0; + int option_index = getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option); - if (c == -1 || c == EOF) { + if (option_index == -1 || option_index == EOF) { break; } - switch (c) { + switch (option_index) { case '?': /* help */ usage5(); case 'h': /* help */ @@ -504,10 +486,11 @@ int process_arguments(int argc, char **argv) { xasprintf(&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); options |= STAT; break; - case 'u': /* user or user id */ + case 'u': /* user or user id */ { + struct passwd *pw; if (is_integer(optarg)) { uid = atoi(optarg); - pw = getpwuid((uid_t)uid); + pw = getpwuid(uid); /* check to be sure user exists */ if (pw == NULL) { usage2(_("UID was not found"), optarg); @@ -521,10 +504,11 @@ int process_arguments(int argc, char **argv) { /* then get uid */ uid = pw->pw_uid; } - user = pw->pw_name; + + char *user = pw->pw_name; xasprintf(&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), uid, user); options |= USER; - break; + } break; case 'C': /* command */ /* TODO: allow this to be passed in with --metric */ if (prog) { @@ -542,12 +526,12 @@ int process_arguments(int argc, char **argv) { exclude_progs = optarg; } xasprintf(&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), exclude_progs); - char *p = strtok(exclude_progs, ","); + char *tmp_pointer = strtok(exclude_progs, ","); - while (p) { + while (tmp_pointer) { exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char *) * ++exclude_progs_counter); - exclude_progs_arr[exclude_progs_counter - 1] = p; - p = strtok(NULL, ","); + exclude_progs_arr[exclude_progs_counter - 1] = tmp_pointer; + tmp_pointer = strtok(NULL, ","); } options |= EXCLUDE_PROGS; @@ -562,23 +546,26 @@ int process_arguments(int argc, char **argv) { xasprintf(&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); options |= ARGS; break; - case CHAR_MAX + 1: - err = regcomp(&re_args, optarg, cflags); + case CHAR_MAX + 1: { + int cflags = REG_NOSUB | REG_EXTENDED; + int err = regcomp(&re_args, optarg, cflags); if (err != 0) { + char errbuf[MAX_INPUT_BUFFER]; regerror(err, &re_args, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } /* Strip off any | within the regex optarg */ - temp_string = strdup(optarg); - while (temp_string[i] != '\0') { - if (temp_string[i] == '|') { - temp_string[i] = ','; + char *temp_string = strdup(optarg); + int index = 0; + while (temp_string[index] != '\0') { + if (temp_string[index] == '|') { + temp_string[index] = ','; } - i++; + index++; } xasprintf(&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); options |= EREG_ARGS; - break; + } break; case 'r': /* RSS */ if (sscanf(optarg, "%d%[^0-9]", &rss, tmp) == 1) { xasprintf(&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); @@ -606,29 +593,33 @@ int process_arguments(int argc, char **argv) { if (strcmp(optarg, "PROCS") == 0) { metric = METRIC_PROCS; break; - } else if (strcmp(optarg, "VSZ") == 0) { + } + if (strcmp(optarg, "VSZ") == 0) { metric = METRIC_VSZ; break; - } else if (strcmp(optarg, "RSS") == 0) { + } + if (strcmp(optarg, "RSS") == 0) { metric = METRIC_RSS; break; - } else if (strcmp(optarg, "CPU") == 0) { + } + if (strcmp(optarg, "CPU") == 0) { metric = METRIC_CPU; break; - } else if (strcmp(optarg, "ELAPSED") == 0) { + } + if (strcmp(optarg, "ELAPSED") == 0) { metric = METRIC_ELAPSED; break; } usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); case 'k': /* linux kernel thread filter */ - kthread_filter = 1; + kthread_filter = true; break; case 'v': /* command */ verbose++; break; case 'T': - usepid = 1; + usepid = true; break; case CHAR_MAX + 2: input_filename = optarg; @@ -636,15 +627,15 @@ int process_arguments(int argc, char **argv) { } } - c = optind; - if ((!warning_range) && argv[c]) { - warning_range = argv[c++]; + int index = optind; + if ((!warning_range) && argv[index]) { + warning_range = argv[index++]; } - if ((!critical_range) && argv[c]) { - critical_range = argv[c++]; + if ((!critical_range) && argv[index]) { + critical_range = argv[index++]; } - if (statopts == NULL && argv[c]) { - xasprintf(&statopts, "%s", argv[c++]); + if (statopts == NULL && argv[index]) { + xasprintf(&statopts, "%s", argv[index++]); xasprintf(&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); options |= STAT; } @@ -685,25 +676,9 @@ int validate_arguments() { /* convert the elapsed time to seconds */ int convert_to_seconds(char *etime) { - - char *ptr; - int total; - - int hyphcnt; - int coloncnt; - int days; - int hours; - int minutes; - int seconds; - - hyphcnt = 0; - coloncnt = 0; - days = 0; - hours = 0; - minutes = 0; - seconds = 0; - - for (ptr = etime; *ptr != '\0'; ptr++) { + int hyphcnt = 0; + int coloncnt = 0; + for (char *ptr = etime; *ptr != '\0'; ptr++) { if (*ptr == '-') { hyphcnt++; @@ -715,6 +690,10 @@ int convert_to_seconds(char *etime) { } } + int days = 0; + int hours = 0; + int minutes = 0; + int seconds = 0; if (hyphcnt > 0) { sscanf(etime, "%d-%d:%d:%d", &days, &hours, &minutes, &seconds); /* linux 2.6.5/2.6.6 reporting some processes with infinite @@ -730,7 +709,7 @@ int convert_to_seconds(char *etime) { } } - total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds; + int total = (days * 86400) + (hours * 3600) + (minutes * 60) + seconds; if (verbose >= 3 && metric == METRIC_ELAPSED) { printf("seconds: %d\n", total); -- cgit v1.2.3-74-g34f1 From a14b2b35776c7550123ce58af571913eb9d7819f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:29:36 +0100 Subject: Refactor check_procs --- plugins/Makefile.am | 1 + plugins/check_procs.c | 335 ++++++++++++++++++++--------------------- plugins/check_procs.d/config.h | 75 +++++++++ 3 files changed, 243 insertions(+), 168 deletions(-) create mode 100644 plugins/check_procs.d/config.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 6c582a15..0920adce 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -63,6 +63,7 @@ EXTRA_DIST = t \ check_mrtg.d \ check_apt.d \ check_pgsql.d \ + check_procs.d \ check_by_ssh.d \ check_smtp.d \ check_mysql.d \ diff --git a/plugins/check_procs.c b/plugins/check_procs.c index da2198a7..83e6864e 100644 --- a/plugins/check_procs.c +++ b/plugins/check_procs.c @@ -44,6 +44,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "utils_cmd.h" #include "regex.h" #include "states.h" +#include "check_procs.d/config.h" #include #include @@ -52,17 +53,17 @@ const char *email = "devel@monitoring-plugins.org"; # include #endif -static int process_arguments(int /*argc*/, char ** /*argv*/); -static int validate_arguments(void); -static int convert_to_seconds(char * /*etime*/); +typedef struct { + int errorcode; + check_procs_config config; +} check_procs_config_wrapper; +static check_procs_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); +static check_procs_config_wrapper validate_arguments(check_procs_config_wrapper /*config_wrapper*/); + +static int convert_to_seconds(char * /*etime*/, enum metric /*metric*/); static void print_help(void); void print_usage(void); -static char *warning_range = NULL; -static char *critical_range = NULL; -static thresholds *procs_thresholds = NULL; - -static int options = 0; /* bitmask of filter criteria to test against */ #define ALL 1 #define STAT 2 #define PPID 4 @@ -80,36 +81,7 @@ static int options = 0; /* bitmask of filter criteria to test against */ "kthreadd" /* the parent process of kernel threads: \ ppid of procs are compared to pid of this proc*/ -/* Different metrics */ -char *metric_name; -enum metric { - METRIC_PROCS, - METRIC_VSZ, - METRIC_RSS, - METRIC_CPU, - METRIC_ELAPSED -}; -enum metric metric = METRIC_PROCS; - static int verbose = 0; -static uid_t uid; -static pid_t ppid; -static int vsz; -static int rss; -static float pcpu; -static char *statopts; -static char *prog; -static char *exclude_progs; -static char **exclude_progs_arr = NULL; -static char exclude_progs_counter = 0; -static char *args; -static char *input_filename = NULL; -static regex_t re_args; -static char *fmt; -static char *fails; -static char tmp[MAX_INPUT_BUFFER]; -static bool kthread_filter = false; -static bool usepid = false; /* whether to test for pid or /proc/pid/exe */ static int stat_exe(const pid_t pid, struct stat *buf) { char *path; @@ -125,27 +97,27 @@ int main(int argc, char **argv) { bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - xasprintf(&metric_name, "PROCS"); - metric = METRIC_PROCS; - /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) { + check_procs_config_wrapper tmp_config = process_arguments(argc, argv); + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } + check_procs_config config = tmp_config.config; + /* find ourself */ pid_t mypid = getpid(); pid_t myppid = getppid(); dev_t mydev = 0; ino_t myino = 0; struct stat statbuf; - if (usepid || stat_exe(mypid, &statbuf) == -1) { + if (config.usepid || stat_exe(mypid, &statbuf) == -1) { /* usepid might have been set by -T */ - usepid = true; + config.usepid = true; } else { - usepid = false; + config.usepid = false; mydev = statbuf.st_dev; myino = statbuf.st_ino; } @@ -163,14 +135,14 @@ int main(int argc, char **argv) { output chld_out; output chld_err; mp_state_enum result = STATE_UNKNOWN; - if (input_filename == NULL) { + if (config.input_filename == NULL) { result = cmd_run(PS_COMMAND, &chld_out, &chld_err, 0); if (chld_err.lines > 0) { printf("%s: %s", _("System call sent warnings to stderr"), chld_err.line[0]); exit(STATE_WARNING); } } else { - result = cmd_file_read(input_filename, &chld_out, 0); + result = cmd_file_read(config.input_filename, &chld_out, 0); } int pos; /* number of spaces before 'args' in `ps` output */ @@ -222,7 +194,7 @@ int main(int argc, char **argv) { strcpy(procprog, base_name(procprog)); /* we need to convert the elapsed time to seconds */ - procseconds = convert_to_seconds(procetime); + procseconds = convert_to_seconds(procetime, config.metric); if (verbose >= 3) { printf("proc#=%d uid=%d vsz=%d rss=%d pid=%d ppid=%d pcpu=%.2f stat=%s etime=%s prog=%s args=%s\n", procs, procuid, procvsz, @@ -231,8 +203,8 @@ int main(int argc, char **argv) { /* Ignore self */ int ret = 0; - if ((usepid && mypid == procpid) || - (((!usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || + if ((config.usepid && mypid == procpid) || + (((!config.usepid) && ((ret = stat_exe(procpid, &statbuf) != -1) && statbuf.st_dev == mydev && statbuf.st_ino == myino)) || (ret == -1 && errno == ENOENT))) { if (verbose >= 3) { printf("not considering - is myself or gone\n"); @@ -256,10 +228,10 @@ int main(int argc, char **argv) { } /* Ignore excluded processes by name */ - if (options & EXCLUDE_PROGS) { + if (config.options & EXCLUDE_PROGS) { bool found = false; - for (int i = 0; i < (exclude_progs_counter); i++) { - if (!strcmp(procprog, exclude_progs_arr[i])) { + for (int i = 0; i < (config.exclude_progs_counter); i++) { + if (!strcmp(procprog, config.exclude_progs_arr[i])) { found = true; } } @@ -275,7 +247,7 @@ int main(int argc, char **argv) { /* filter kernel threads (children of KTHREAD_PARENT)*/ /* TODO adapt for other OSes than GNU/Linux sorry for not doing that, but I've no other OSes to test :-( */ - if (kthread_filter) { + if (config.kthread_filter) { /* get pid KTHREAD_PARENT */ if (kthread_ppid == 0 && !strcmp(procprog, KTHREAD_PARENT)) { kthread_ppid = procpid; @@ -289,38 +261,38 @@ int main(int argc, char **argv) { } } - if ((options & STAT) && (strstr(procstat, statopts))) { + if ((config.options & STAT) && (strstr(procstat, config.statopts))) { resultsum |= STAT; } - if ((options & ARGS) && procargs && (strstr(procargs, args) != NULL)) { + if ((config.options & ARGS) && procargs && (strstr(procargs, config.args) != NULL)) { resultsum |= ARGS; } - if ((options & EREG_ARGS) && procargs && (regexec(&re_args, procargs, (size_t)0, NULL, 0) == 0)) { + if ((config.options & EREG_ARGS) && procargs && (regexec(&config.re_args, procargs, (size_t)0, NULL, 0) == 0)) { resultsum |= EREG_ARGS; } - if ((options & PROG) && procprog && (strcmp(prog, procprog) == 0)) { + if ((config.options & PROG) && procprog && (strcmp(config.prog, procprog) == 0)) { resultsum |= PROG; } - if ((options & PPID) && (procppid == ppid)) { + if ((config.options & PPID) && (procppid == config.ppid)) { resultsum |= PPID; } - if ((options & USER) && (procuid == uid)) { + if ((config.options & USER) && (procuid == config.uid)) { resultsum |= USER; } - if ((options & VSZ) && (procvsz >= vsz)) { + if ((config.options & VSZ) && (procvsz >= config.vsz)) { resultsum |= VSZ; } - if ((options & RSS) && (procrss >= rss)) { + if ((config.options & RSS) && (procrss >= config.rss)) { resultsum |= RSS; } - if ((options & PCPU) && (procpcpu >= pcpu)) { + if ((config.options & PCPU) && (procpcpu >= config.pcpu)) { resultsum |= PCPU; } found++; /* Next line if filters not matched */ - if (!(options == resultsum || options == ALL)) { + if (!(config.options == resultsum || config.options == ALL)) { continue; } @@ -331,27 +303,27 @@ int main(int argc, char **argv) { } mp_state_enum temporary_result = STATE_OK; - if (metric == METRIC_VSZ) { - temporary_result = get_status((double)procvsz, procs_thresholds); - } else if (metric == METRIC_RSS) { - temporary_result = get_status((double)procrss, procs_thresholds); + if (config.metric == METRIC_VSZ) { + temporary_result = get_status((double)procvsz, config.procs_thresholds); + } else if (config.metric == METRIC_RSS) { + temporary_result = get_status((double)procrss, config.procs_thresholds); } /* TODO? float thresholds for --metric=CPU */ - else if (metric == METRIC_CPU) { - temporary_result = get_status(procpcpu, procs_thresholds); - } else if (metric == METRIC_ELAPSED) { - temporary_result = get_status((double)procseconds, procs_thresholds); + else if (config.metric == METRIC_CPU) { + temporary_result = get_status(procpcpu, config.procs_thresholds); + } else if (config.metric == METRIC_ELAPSED) { + temporary_result = get_status((double)procseconds, config.procs_thresholds); } - if (metric != METRIC_PROCS) { + if (config.metric != METRIC_PROCS) { if (temporary_result == STATE_WARNING) { warn++; - xasprintf(&fails, "%s%s%s", fails, (strcmp(fails, "") ? ", " : ""), procprog); + xasprintf(&config.fails, "%s%s%s", config.fails, (strcmp(config.fails, "") ? ", " : ""), procprog); result = max_state(result, temporary_result); } if (temporary_result == STATE_CRITICAL) { crit++; - xasprintf(&fails, "%s%s%s", fails, (strcmp(fails, "") ? ", " : ""), procprog); + xasprintf(&config.fails, "%s%s%s", config.fails, (strcmp(config.fails, "") ? ", " : ""), procprog); result = max_state(result, temporary_result); } } @@ -372,35 +344,36 @@ int main(int argc, char **argv) { } /* Needed if procs found, but none match filter */ - if (metric == METRIC_PROCS) { - result = max_state(result, get_status((double)procs, procs_thresholds)); + if (config.metric == METRIC_PROCS) { + result = max_state(result, get_status((double)procs, config.procs_thresholds)); } if (result == STATE_OK) { - printf("%s %s: ", metric_name, _("OK")); + printf("%s %s: ", config.metric_name, _("OK")); } else if (result == STATE_WARNING) { - printf("%s %s: ", metric_name, _("WARNING")); - if (metric != METRIC_PROCS) { + printf("%s %s: ", config.metric_name, _("WARNING")); + if (config.metric != METRIC_PROCS) { printf(_("%d warn out of "), warn); } } else if (result == STATE_CRITICAL) { - printf("%s %s: ", metric_name, _("CRITICAL")); - if (metric != METRIC_PROCS) { + printf("%s %s: ", config.metric_name, _("CRITICAL")); + if (config.metric != METRIC_PROCS) { printf(_("%d crit, %d warn out of "), crit, warn); } } printf(ngettext("%d process", "%d processes", (unsigned long)procs), procs); - if (strcmp(fmt, "") != 0) { - printf(_(" with %s"), fmt); + if (strcmp(config.fmt, "") != 0) { + printf(_(" with %s"), config.fmt); } - if (verbose >= 1 && strcmp(fails, "")) { - printf(" [%s]", fails); + if (verbose >= 1 && strcmp(config.fails, "")) { + printf(" [%s]", config.fails); } - if (metric == METRIC_PROCS) { - printf(" | procs=%d;%s;%s;0;", procs, warning_range ? warning_range : "", critical_range ? critical_range : ""); + if (config.metric == METRIC_PROCS) { + printf(" | procs=%d;%s;%s;0;", procs, config.warning_range ? config.warning_range : "", + config.critical_range ? config.critical_range : ""); } else { printf(" | procs=%d;;;0; procs_warn=%d;;;0; procs_crit=%d;;;0;", procs, warn, crit); } @@ -410,7 +383,7 @@ int main(int argc, char **argv) { } /* process command-line arguments */ -int process_arguments(int argc, char **argv) { +check_procs_config_wrapper process_arguments(int argc, char **argv) { static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"metric", required_argument, 0, 'm'}, @@ -440,6 +413,11 @@ int process_arguments(int argc, char **argv) { } } + check_procs_config_wrapper result = { + .errorcode = OK, + .config = check_procs_config_init(), + }; + while (true) { int option = 0; int option_index = getopt_long(argc, argv, "Vvhkt:c:w:p:s:u:C:a:z:r:m:P:T:X:", longopts, &option); @@ -465,32 +443,36 @@ int process_arguments(int argc, char **argv) { } break; case 'c': /* critical threshold */ - critical_range = optarg; + result.config.critical_range = optarg; break; case 'w': /* warning threshold */ - warning_range = optarg; + result.config.warning_range = optarg; break; - case 'p': /* process id */ - if (sscanf(optarg, "%d%[^0-9]", &ppid, tmp) == 1) { - xasprintf(&fmt, "%s%sPPID = %d", (fmt ? fmt : ""), (options ? ", " : ""), ppid); - options |= PPID; + case 'p': { /* process id */ + static char tmp[MAX_INPUT_BUFFER]; + if (sscanf(optarg, "%d%[^0-9]", &result.config.ppid, tmp) == 1) { + xasprintf(&result.config.fmt, "%s%sPPID = %d", (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.ppid); + result.config.options |= PPID; break; } usage4(_("Parent Process ID must be an integer!")); + } case 's': /* status */ - if (statopts) { + if (result.config.statopts) { break; } else { - statopts = optarg; + result.config.statopts = optarg; } - xasprintf(&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); - options |= STAT; + xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.statopts); + result.config.options |= STAT; break; case 'u': /* user or user id */ { struct passwd *pw; if (is_integer(optarg)) { - uid = atoi(optarg); - pw = getpwuid(uid); + result.config.uid = atoi(optarg); + pw = getpwuid(result.config.uid); /* check to be sure user exists */ if (pw == NULL) { usage2(_("UID was not found"), optarg); @@ -502,56 +484,61 @@ int process_arguments(int argc, char **argv) { usage2(_("User name was not found"), optarg); } /* then get uid */ - uid = pw->pw_uid; + result.config.uid = pw->pw_uid; } char *user = pw->pw_name; - xasprintf(&fmt, "%s%sUID = %d (%s)", (fmt ? fmt : ""), (options ? ", " : ""), uid, user); - options |= USER; + xasprintf(&result.config.fmt, "%s%sUID = %d (%s)", (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.uid, user); + result.config.options |= USER; } break; case 'C': /* command */ /* TODO: allow this to be passed in with --metric */ - if (prog) { + if (result.config.prog) { break; } else { - prog = optarg; + result.config.prog = optarg; } - xasprintf(&fmt, _("%s%scommand name '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), prog); - options |= PROG; + xasprintf(&result.config.fmt, _("%s%scommand name '%s'"), (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.prog); + result.config.options |= PROG; break; case 'X': - if (exclude_progs) { + if (result.config.exclude_progs) { break; } else { - exclude_progs = optarg; + result.config.exclude_progs = optarg; } - xasprintf(&fmt, _("%s%sexclude progs '%s'"), (fmt ? fmt : ""), (options ? ", " : ""), exclude_progs); - char *tmp_pointer = strtok(exclude_progs, ","); + xasprintf(&result.config.fmt, _("%s%sexclude progs '%s'"), (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.exclude_progs); + char *tmp_pointer = strtok(result.config.exclude_progs, ","); while (tmp_pointer) { - exclude_progs_arr = realloc(exclude_progs_arr, sizeof(char *) * ++exclude_progs_counter); - exclude_progs_arr[exclude_progs_counter - 1] = tmp_pointer; + result.config.exclude_progs_arr = + realloc(result.config.exclude_progs_arr, sizeof(char *) * ++result.config.exclude_progs_counter); + result.config.exclude_progs_arr[result.config.exclude_progs_counter - 1] = tmp_pointer; tmp_pointer = strtok(NULL, ","); } - options |= EXCLUDE_PROGS; + result.config.options |= EXCLUDE_PROGS; break; case 'a': /* args (full path name with args) */ /* TODO: allow this to be passed in with --metric */ - if (args) { + if (result.config.args) { break; } else { - args = optarg; + result.config.args = optarg; } - xasprintf(&fmt, "%s%sargs '%s'", (fmt ? fmt : ""), (options ? ", " : ""), args); - options |= ARGS; + xasprintf(&result.config.fmt, "%s%sargs '%s'", (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.args); + result.config.options |= ARGS; break; case CHAR_MAX + 1: { int cflags = REG_NOSUB | REG_EXTENDED; - int err = regcomp(&re_args, optarg, cflags); + int err = regcomp(&result.config.re_args, optarg, cflags); if (err != 0) { char errbuf[MAX_INPUT_BUFFER]; - regerror(err, &re_args, errbuf, MAX_INPUT_BUFFER); + regerror(err, &result.config.re_args, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "PROCS %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } /* Strip off any | within the regex optarg */ @@ -563,119 +550,131 @@ int process_arguments(int argc, char **argv) { } index++; } - xasprintf(&fmt, "%s%sregex args '%s'", (fmt ? fmt : ""), (options ? ", " : ""), temp_string); - options |= EREG_ARGS; + xasprintf(&result.config.fmt, "%s%sregex args '%s'", (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), temp_string); + result.config.options |= EREG_ARGS; } break; - case 'r': /* RSS */ - if (sscanf(optarg, "%d%[^0-9]", &rss, tmp) == 1) { - xasprintf(&fmt, "%s%sRSS >= %d", (fmt ? fmt : ""), (options ? ", " : ""), rss); - options |= RSS; + case 'r': { /* RSS */ + static char tmp[MAX_INPUT_BUFFER]; + if (sscanf(optarg, "%d%[^0-9]", &result.config.rss, tmp) == 1) { + xasprintf(&result.config.fmt, "%s%sRSS >= %d", (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.rss); + result.config.options |= RSS; break; } usage4(_("RSS must be an integer!")); - case 'z': /* VSZ */ - if (sscanf(optarg, "%d%[^0-9]", &vsz, tmp) == 1) { - xasprintf(&fmt, "%s%sVSZ >= %d", (fmt ? fmt : ""), (options ? ", " : ""), vsz); - options |= VSZ; + } + case 'z': { /* VSZ */ + static char tmp[MAX_INPUT_BUFFER]; + if (sscanf(optarg, "%d%[^0-9]", &result.config.vsz, tmp) == 1) { + xasprintf(&result.config.fmt, "%s%sVSZ >= %d", (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.vsz); + result.config.options |= VSZ; break; } usage4(_("VSZ must be an integer!")); - case 'P': /* PCPU */ + } + case 'P': { /* PCPU */ /* TODO: -P 1.5.5 is accepted */ - if (sscanf(optarg, "%f%[^0-9.]", &pcpu, tmp) == 1) { - xasprintf(&fmt, "%s%sPCPU >= %.2f", (fmt ? fmt : ""), (options ? ", " : ""), pcpu); - options |= PCPU; + static char tmp[MAX_INPUT_BUFFER]; + if (sscanf(optarg, "%f%[^0-9.]", &result.config.pcpu, tmp) == 1) { + xasprintf(&result.config.fmt, "%s%sPCPU >= %.2f", (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.pcpu); + result.config.options |= PCPU; break; } usage4(_("PCPU must be a float!")); + } case 'm': - xasprintf(&metric_name, "%s", optarg); + xasprintf(&result.config.metric_name, "%s", optarg); if (strcmp(optarg, "PROCS") == 0) { - metric = METRIC_PROCS; + result.config.metric = METRIC_PROCS; break; } if (strcmp(optarg, "VSZ") == 0) { - metric = METRIC_VSZ; + result.config.metric = METRIC_VSZ; break; } if (strcmp(optarg, "RSS") == 0) { - metric = METRIC_RSS; + result.config.metric = METRIC_RSS; break; } if (strcmp(optarg, "CPU") == 0) { - metric = METRIC_CPU; + result.config.metric = METRIC_CPU; break; } if (strcmp(optarg, "ELAPSED") == 0) { - metric = METRIC_ELAPSED; + result.config.metric = METRIC_ELAPSED; break; } usage4(_("Metric must be one of PROCS, VSZ, RSS, CPU, ELAPSED!")); case 'k': /* linux kernel thread filter */ - kthread_filter = true; + result.config.kthread_filter = true; break; case 'v': /* command */ verbose++; break; case 'T': - usepid = true; + result.config.usepid = true; break; case CHAR_MAX + 2: - input_filename = optarg; + result.config.input_filename = optarg; break; } } int index = optind; - if ((!warning_range) && argv[index]) { - warning_range = argv[index++]; + if ((!result.config.warning_range) && argv[index]) { + result.config.warning_range = argv[index++]; } - if ((!critical_range) && argv[index]) { - critical_range = argv[index++]; + if ((!result.config.critical_range) && argv[index]) { + result.config.critical_range = argv[index++]; } - if (statopts == NULL && argv[index]) { - xasprintf(&statopts, "%s", argv[index++]); - xasprintf(&fmt, _("%s%sSTATE = %s"), (fmt ? fmt : ""), (options ? ", " : ""), statopts); - options |= STAT; + if (result.config.statopts == NULL && argv[index]) { + xasprintf(&result.config.statopts, "%s", argv[index++]); + xasprintf(&result.config.fmt, _("%s%sSTATE = %s"), (result.config.fmt ? result.config.fmt : ""), + (result.config.options ? ", " : ""), result.config.statopts); + result.config.options |= STAT; } /* this will abort in case of invalid ranges */ - set_thresholds(&procs_thresholds, warning_range, critical_range); + set_thresholds(&result.config.procs_thresholds, result.config.warning_range, result.config.critical_range); - return validate_arguments(); + return validate_arguments(result); } -int validate_arguments() { - if (options == 0) { - options = ALL; +check_procs_config_wrapper validate_arguments(check_procs_config_wrapper config_wrapper) { + if (config_wrapper.config.options == 0) { + config_wrapper.config.options = ALL; } - if (statopts == NULL) { - statopts = strdup(""); + if (config_wrapper.config.statopts == NULL) { + config_wrapper.config.statopts = strdup(""); } - if (prog == NULL) { - prog = strdup(""); + if (config_wrapper.config.prog == NULL) { + config_wrapper.config.prog = strdup(""); } - if (args == NULL) { - args = strdup(""); + if (config_wrapper.config.args == NULL) { + config_wrapper.config.args = strdup(""); } - if (fmt == NULL) { - fmt = strdup(""); + if (config_wrapper.config.fmt == NULL) { + config_wrapper.config.fmt = strdup(""); } - if (fails == NULL) { - fails = strdup(""); + if (config_wrapper.config.fails == NULL) { + config_wrapper.config.fails = strdup(""); } - return options; + // return options; + return config_wrapper; } /* convert the elapsed time to seconds */ -int convert_to_seconds(char *etime) { +int convert_to_seconds(char *etime, enum metric metric) { int hyphcnt = 0; int coloncnt = 0; for (char *ptr = etime; *ptr != '\0'; ptr++) { diff --git a/plugins/check_procs.d/config.h b/plugins/check_procs.d/config.h new file mode 100644 index 00000000..815809d4 --- /dev/null +++ b/plugins/check_procs.d/config.h @@ -0,0 +1,75 @@ +#pragma once + +#include "../../config.h" +#include "regex.h" +#include "thresholds.h" +#include +#include +#include + +enum metric { + METRIC_PROCS, + METRIC_VSZ, + METRIC_RSS, + METRIC_CPU, + METRIC_ELAPSED +}; + +typedef struct { + int options; /* bitmask of filter criteria to test against */ + enum metric metric; + char *metric_name; + char *input_filename; + char *prog; + char *args; + char *fmt; + char *fails; + char *exclude_progs; + char **exclude_progs_arr; + char exclude_progs_counter; + regex_t re_args; + + bool kthread_filter; + bool usepid; /* whether to test for pid or /proc/pid/exe */ + uid_t uid; + pid_t ppid; + int vsz; + int rss; + float pcpu; + char *statopts; + + char *warning_range; + char *critical_range; + thresholds *procs_thresholds; +} check_procs_config; + +check_procs_config check_procs_config_init() { + check_procs_config tmp = { + .options = 0, + .metric = METRIC_PROCS, + .metric_name = strdup("PROCS"), + .input_filename = NULL, + .prog = NULL, + .args = NULL, + .fmt = NULL, + .fails = NULL, + .exclude_progs = NULL, + .exclude_progs_arr = NULL, + .exclude_progs_counter = 0, + .re_args = {}, + + .kthread_filter = false, + .usepid = false, + .uid = {}, + .ppid = {}, + .vsz = 0, + .rss = 0, + .pcpu = 0, + .statopts = NULL, + + .warning_range = NULL, + .critical_range = NULL, + .procs_thresholds = NULL, + }; + return tmp; +} -- cgit v1.2.3-74-g34f1 From a746576b8cb72a3233caf5ac852b2679cc98d80c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:50:50 +0100 Subject: Fix initialisers for old compilers --- plugins/check_procs.d/config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/check_procs.d/config.h b/plugins/check_procs.d/config.h index 815809d4..e32ca066 100644 --- a/plugins/check_procs.d/config.h +++ b/plugins/check_procs.d/config.h @@ -56,12 +56,12 @@ check_procs_config check_procs_config_init() { .exclude_progs = NULL, .exclude_progs_arr = NULL, .exclude_progs_counter = 0, - .re_args = {}, + .re_args = {0}, .kthread_filter = false, .usepid = false, - .uid = {}, - .ppid = {}, + .uid = 0, + .ppid = 0, .vsz = 0, .rss = 0, .pcpu = 0, -- cgit v1.2.3-74-g34f1 From 39b9b62adda3887e7706754d4e34a7eff2f793b5 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 21:34:58 +0100 Subject: check_users: clang-format --- plugins/check_users.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/plugins/check_users.c b/plugins/check_users.c index f1e1c39d..e91ed4a0 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -83,15 +83,16 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) + if (process_arguments(argc, argv) == ERROR) { usage4(_("Could not parse arguments")); + } users = 0; #ifdef HAVE_LIBSYSTEMD - if (sd_booted() > 0) + if (sd_booted() > 0) { users = sd_get_sessions(NULL); - else { + } else { #endif #if HAVE_WTSAPI32_H if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { @@ -104,18 +105,21 @@ int main(int argc, char **argv) { DWORD size; int len; - if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) + if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) { continue; + } len = lstrlen(username); WTSFreeMemory(username); - if (len == 0) + if (len == 0) { continue; + } - if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) + if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { users++; + } } WTSFreeMemory(wtsinfo); @@ -123,9 +127,11 @@ int main(int argc, char **argv) { /* get currently logged users from utmpx */ setutxent(); - while ((putmpx = getutxent()) != NULL) - if (putmpx->ut_type == USER_PROCESS) + while ((putmpx = getutxent()) != NULL) { + if (putmpx->ut_type == USER_PROCESS) { users++; + } + } endutxent(); #else @@ -137,8 +143,9 @@ int main(int argc, char **argv) { } child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); - if (child_stderr == NULL) + if (child_stderr == NULL) { printf(_("Could not open stderr for %s\n"), WHO_COMMAND); + } while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { /* increment 'users' on all lines except total user count */ @@ -148,18 +155,21 @@ int main(int argc, char **argv) { } /* get total logged in users */ - if (sscanf(input_buffer, _("# users=%d"), &users) == 1) + if (sscanf(input_buffer, _("# users=%d"), &users) == 1) { break; + } } /* check STDERR */ - if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) + if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { result = possibly_set(result, STATE_UNKNOWN); + } (void)fclose(child_stderr); /* close the pipe */ - if (spclose(child_process)) + if (spclose(child_process)) { result = possibly_set(result, STATE_UNKNOWN); + } #endif #ifdef HAVE_LIBSYSTEMD } @@ -168,9 +178,9 @@ int main(int argc, char **argv) { /* check the user count against warning and critical thresholds */ result = get_status((double)users, thlds); - if (result == STATE_UNKNOWN) + if (result == STATE_UNKNOWN) { printf("%s\n", _("Unable to read output")); - else { + } else { printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); } @@ -186,16 +196,18 @@ int process_arguments(int argc, char **argv) { {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; - if (argc < 2) + if (argc < 2) { usage("\n"); + } int option_char; while (true) { int option = 0; option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option); - if (option_char == -1 || option_char == EOF || option_char == 1) + if (option_char == -1 || option_char == EOF || option_char == 1) { break; + } switch (option_char) { case '?': /* print short usage statement if args not parsable */ @@ -217,11 +229,13 @@ int process_arguments(int argc, char **argv) { option_char = optind; - if (warning_range == NULL && argc > option_char) + if (warning_range == NULL && argc > option_char) { warning_range = argv[option_char++]; + } - if (critical_range == NULL && argc > option_char) + if (critical_range == NULL && argc > option_char) { critical_range = argv[option_char++]; + } /* this will abort in case of invalid ranges */ set_thresholds(&thlds, warning_range, critical_range); -- cgit v1.2.3-74-g34f1 From 6ac236c1ef06ef17541d3919aed2a008aabaa7f4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:01:46 +0100 Subject: Refactor check_users --- plugins/Makefile.am | 2 + plugins/check_users.c | 250 +++++++++++++++++++---------------------- plugins/check_users.d/config.h | 20 ++++ plugins/check_users.d/users.c | 166 +++++++++++++++++++++++++++ plugins/check_users.d/users.h | 18 +++ 5 files changed, 319 insertions(+), 137 deletions(-) create mode 100644 plugins/check_users.d/config.h create mode 100644 plugins/check_users.d/users.c create mode 100644 plugins/check_users.d/users.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 5cd20319..0a2a91aa 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -55,6 +55,7 @@ EXTRA_DIST = t \ check_game.d \ check_radius.d \ check_time.d \ + check_users.d \ check_nagios.d \ check_dbi.d \ check_real.d \ @@ -152,6 +153,7 @@ check_tcp_LDADD = $(SSLOBJS) check_time_LDADD = $(NETLIBS) check_ntp_time_LDADD = $(NETLIBS) $(MATHLIBS) check_ups_LDADD = $(NETLIBS) +check_users_SOURCES = check_users.c check_users.d/users.c check_users_LDADD = $(BASEOBJS) $(WTSAPI32LIBS) $(SYSTEMDLIBS) check_by_ssh_LDADD = $(NETLIBS) check_ide_smart_LDADD = $(BASEOBJS) diff --git a/plugins/check_users.c b/plugins/check_users.c index e91ed4a0..61427f97 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -34,8 +34,15 @@ const char *progname = "check_users"; const char *copyright = "2000-2024"; const char *email = "devel@monitoring-plugins.org"; -#include "common.h" -#include "utils.h" +#include "check_users.d/users.h" +#include "output.h" +#include "perfdata.h" +#include "states.h" +#include "utils_base.h" +#include "./common.h" +#include "./utils.h" +#include "check_users.d/config.h" +#include "thresholds.h" #if HAVE_WTSAPI32_H # include @@ -53,29 +60,16 @@ const char *email = "devel@monitoring-plugins.org"; # include #endif -#define possibly_set(a, b) ((a) == 0 ? (b) : 0) +typedef struct process_argument_wrapper { + int errorcode; + check_users_config config; +} check_users_config_wrapper; +check_users_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static int process_arguments(int, char **); -static void print_help(void); +void print_help(void); void print_usage(void); -static char *warning_range = NULL; -static char *critical_range = NULL; -static thresholds *thlds = NULL; - int main(int argc, char **argv) { - int users = -1; - int result = STATE_UNKNOWN; -#if HAVE_WTSAPI32_H - WTS_SESSION_INFO *wtsinfo; - DWORD wtscount; - DWORD index; -#elif HAVE_UTMPX_H - struct utmpx *putmpx; -#else - char input_buffer[MAX_INPUT_BUFFER]; -#endif - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -83,133 +77,100 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) { + check_users_config_wrapper tmp_config = process_arguments(argc, argv); + + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } - users = 0; - -#ifdef HAVE_LIBSYSTEMD - if (sd_booted() > 0) { - users = sd_get_sessions(NULL); - } else { -#endif -#if HAVE_WTSAPI32_H - if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { - printf(_("Could not enumerate RD sessions: %d\n"), GetLastError()); - return STATE_UNKNOWN; - } - - for (index = 0; index < wtscount; index++) { - LPTSTR username; - DWORD size; - int len; - - if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) { - continue; - } - - len = lstrlen(username); - - WTSFreeMemory(username); - - if (len == 0) { - continue; - } - - if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { - users++; - } - } + check_users_config config = tmp_config.config; - WTSFreeMemory(wtsinfo); -#elif HAVE_UTMPX_H - /* get currently logged users from utmpx */ - setutxent(); - - while ((putmpx = getutxent()) != NULL) { - if (putmpx->ut_type == USER_PROCESS) { - users++; - } - } - - endutxent(); +#ifdef _WIN32 +# if HAVE_WTSAPI32_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_windows(); +# else +# error Did not find WTSAPI32 +# endif // HAVE_WTSAPI32_H #else - /* run the command */ - child_process = spopen(WHO_COMMAND); - if (child_process == NULL) { - printf(_("Could not open pipe: %s\n"), WHO_COMMAND); - return STATE_UNKNOWN; - } - - child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); - if (child_stderr == NULL) { - printf(_("Could not open stderr for %s\n"), WHO_COMMAND); - } - - while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { - /* increment 'users' on all lines except total user count */ - if (input_buffer[0] != '#') { - users++; - continue; - } - - /* get total logged in users */ - if (sscanf(input_buffer, _("# users=%d"), &users) == 1) { - break; - } - } - - /* check STDERR */ - if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { - result = possibly_set(result, STATE_UNKNOWN); +# ifdef HAVE_LIBSYSTEMD + get_num_of_users_wrapper user_wrapper = get_num_of_users_systemd(); +# elif HAVE_UTMPX_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_utmp(); +# else // !HAVE_LIBSYSTEMD && !HAVE_UTMPX_H + get_num_of_users_wrapper user_wrapper = get_num_of_users_who_command(); +# endif // HAVE_LIBSYSTEMD +#endif // _WIN32 + + mp_check overall = mp_check_init(); + if (config.output_format_is_set) { + mp_set_format(config.output_format); } - (void)fclose(child_stderr); + mp_subcheck sc_users = mp_subcheck_init(); - /* close the pipe */ - if (spclose(child_process)) { - result = possibly_set(result, STATE_UNKNOWN); + if (user_wrapper.errorcode != 0) { + sc_users = mp_set_subcheck_state(sc_users, STATE_UNKNOWN); + sc_users.output = "Failed to retrieve number of users"; + mp_add_subcheck_to_check(&overall, sc_users); + mp_exit(overall); } -#endif -#ifdef HAVE_LIBSYSTEMD - } -#endif - /* check the user count against warning and critical thresholds */ - result = get_status((double)users, thlds); - if (result == STATE_UNKNOWN) { - printf("%s\n", _("Unable to read output")); - } else { - printf(_("USERS %s - %d users currently logged in |%s\n"), state_text(result), users, - sperfdata_int("users", users, "", warning_range, critical_range, true, 0, false, 0)); + mp_perfdata users_pd = { + .label = "users", + .value = mp_create_pd_value(user_wrapper.users), + }; + + users_pd = mp_pd_set_thresholds(users_pd, config.thresholds); + mp_add_perfdata_to_subcheck(&sc_users, users_pd); + + int tmp_status = mp_get_pd_status(users_pd); + sc_users = mp_set_subcheck_state(sc_users, tmp_status); + + switch (tmp_status) { + case STATE_WARNING: + xasprintf(&sc_users.output, "%d users currently logged in. This violates the warning threshold", user_wrapper.users); + break; + case STATE_CRITICAL: + xasprintf(&sc_users.output, "%d users currently logged in. This violates the critical threshold", user_wrapper.users); + break; + default: + xasprintf(&sc_users.output, "%d users currently logged in", user_wrapper.users); } - return result; + mp_add_subcheck_to_check(&overall, sc_users); + mp_exit(overall); } +#define output_format_index CHAR_MAX + 1 + /* process command-line arguments */ -int process_arguments(int argc, char **argv) { +check_users_config_wrapper process_arguments(int argc, char **argv) { static struct option longopts[] = {{"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; if (argc < 2) { - usage("\n"); + usage(progname); } - int option_char; + char *warning_range = NULL; + char *critical_range = NULL; + check_users_config_wrapper result = { + .config = check_users_config_init(), + .errorcode = OK, + }; + while (true) { - int option = 0; - option_char = getopt_long(argc, argv, "+hVvc:w:", longopts, &option); + int counter = getopt_long(argc, argv, "+hVvc:w:", longopts, NULL); - if (option_char == -1 || option_char == EOF || option_char == 1) { + if (counter == -1 || counter == EOF || counter == 1) { break; } - switch (option_char) { + switch (counter) { case '?': /* print short usage statement if args not parsable */ usage5(); case 'h': /* help */ @@ -224,31 +185,45 @@ int process_arguments(int argc, char **argv) { case 'w': /* warning */ warning_range = optarg; break; - } - } - - option_char = optind; - - if (warning_range == NULL && argc > option_char) { - warning_range = argv[option_char++]; - } + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } - if (critical_range == NULL && argc > option_char) { - critical_range = argv[option_char++]; + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } + } } - /* this will abort in case of invalid ranges */ - set_thresholds(&thlds, warning_range, critical_range); - - if (!thlds->warning) { - usage4(_("Warning threshold must be a valid range expression")); + // TODO add proper verification for ranges here! + if (warning_range) { + mp_range_parsed tmp = mp_parse_range_string(warning_range); + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.warning = tmp.range; + result.config.thresholds.warning_is_set = true; + } else { + printf("Failed to parse warning range: %s", warning_range); + exit(STATE_UNKNOWN); + } } - if (!thlds->critical) { - usage4(_("Critical threshold must be a valid range expression")); + if (critical_range) { + mp_range_parsed tmp = mp_parse_range_string(critical_range); + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.critical = tmp.range; + result.config.thresholds.critical_is_set = true; + } else { + printf("Failed to parse critical range: %s", critical_range); + exit(STATE_UNKNOWN); + } } - return OK; + return result; } void print_help(void) { @@ -271,6 +246,7 @@ void print_help(void) { printf(" %s\n", _("Set WARNING status if number of logged in users violates RANGE_EXPRESSION")); printf(" %s\n", "-c, --critical=RANGE_EXPRESSION"); printf(" %s\n", _("Set CRITICAL status if number of logged in users violates RANGE_EXPRESSION")); + printf(UT_OUTPUT_FORMAT); printf(UT_SUPPORT); } diff --git a/plugins/check_users.d/config.h b/plugins/check_users.d/config.h new file mode 100644 index 00000000..26d3ee70 --- /dev/null +++ b/plugins/check_users.d/config.h @@ -0,0 +1,20 @@ +#pragma once + +#include "output.h" +#include "thresholds.h" + +typedef struct check_users_config { + mp_thresholds thresholds; + + bool output_format_is_set; + mp_output_format output_format; +} check_users_config; + +check_users_config check_users_config_init() { + check_users_config tmp = { + .thresholds = mp_thresholds_init(), + + .output_format_is_set = false, + }; + return tmp; +} diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c new file mode 100644 index 00000000..7969ae79 --- /dev/null +++ b/plugins/check_users.d/users.c @@ -0,0 +1,166 @@ +#include "./users.h" + +#ifdef _WIN32 +# ifdef HAVE_WTSAPI32_H +# include +# include +# undef ERROR +# define ERROR -1 + +get_num_of_users_wrapper get_num_of_users_windows() { + WTS_SESSION_INFO *wtsinfo; + DWORD wtscount; + + get_num_of_users_wrapper result = {}; + + if (!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &wtsinfo, &wtscount)) { + // printf(_("Could not enumerate RD sessions: %d\n"), GetLastError()); + result.error = WINDOWS_COULD_NOT_ENUMERATE_SESSIONS; + return result; + } + + for (DWORD index = 0; index < wtscount; index++) { + LPTSTR username; + DWORD size; + + if (!WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, wtsinfo[index].SessionId, WTSUserName, &username, &size)) { + continue; + } + + int len = lstrlen(username); + + WTSFreeMemory(username); + + if (len == 0) { + continue; + } + + if (wtsinfo[index].State == WTSActive || wtsinfo[index].State == WTSDisconnected) { + result.users++; + } + } + + WTSFreeMemory(wtsinfo); + return result; +} +# else // HAVE_WTSAPI32_H +# error On windows but without the WTSAPI32 lib +# endif // HAVE_WTSAPI32_H + +#else // _WIN32 + +# include "../../config.h" + +# ifdef HAVE_LIBSYSTEMD +# include +# include + +get_num_of_users_wrapper get_num_of_users_systemd() { + get_num_of_users_wrapper result = {}; + + // Test whether we booted with systemd + if (sd_booted() > 0) { + int users = sd_get_sessions(NULL); + if (users >= 0) { + // Success + result.users = users; + return result; + } + + // Failure! return the error code + result.errorcode = users; + return result; + } + + // Looks like we are not running systemd, + // return with error here + result.errorcode = NO_SYSTEMD_ERROR; + return result; +} +# endif + +# ifdef HAVE_UTMPX_H +# include + +get_num_of_users_wrapper get_num_of_users_utmp() { + int users = 0; + + /* get currently logged users from utmpx */ + setutxent(); + + struct utmpx *putmpx; + while ((putmpx = getutxent()) != NULL) { + if (putmpx->ut_type == USER_PROCESS) { + users++; + } + } + + endutxent(); + + get_num_of_users_wrapper result = { + .errorcode = 0, + .users = users, + }; + + return result; +} +# endif + +# ifndef HAVE_WTSAPI32_H +# ifndef HAVE_LIBSYSTEMD +# ifndef HAVE_UTMPX_H +// Fall back option here for the others (probably still not on windows) + +# include "../popen.h" +# include "../common.h" +# include "../utils.h" + +get_num_of_users_wrapper get_num_of_users_who_command() { + /* run the command */ + child_process = spopen(WHO_COMMAND); + if (child_process == NULL) { + // printf(_("Could not open pipe: %s\n"), WHO_COMMAND); + get_num_of_users_wrapper result = { + .errorcode = COULD_NOT_OPEN_PIPE, + }; + return result; + } + + child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); + if (child_stderr == NULL) { + // printf(_("Could not open stderr for %s\n"), WHO_COMMAND); + // TODO this error should probably be reported + } + + get_num_of_users_wrapper result = {}; + char input_buffer[MAX_INPUT_BUFFER]; + while (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process)) { + /* increment 'users' on all lines except total user count */ + if (input_buffer[0] != '#') { + result.users++; + continue; + } + + /* get total logged in users */ + if (sscanf(input_buffer, _("# users=%d"), &result.users) == 1) { + break; + } + } + + /* check STDERR */ + if (fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) { + // if this fails, something broke and the result can not be relied upon or so is the theorie here + result.errorcode = STDERR_COULD_NOT_BE_READ; + } + (void)fclose(child_stderr); + + /* close the pipe */ + spclose(child_process); + + return result; +} + +# endif +# endif +# endif +#endif diff --git a/plugins/check_users.d/users.h b/plugins/check_users.d/users.h new file mode 100644 index 00000000..aacba775 --- /dev/null +++ b/plugins/check_users.d/users.h @@ -0,0 +1,18 @@ +#pragma once + +typedef struct get_num_of_users_wrapper { + int errorcode; + int users; +} get_num_of_users_wrapper; + +enum { + NO_SYSTEMD_ERROR = 64, + WINDOWS_COULD_NOT_ENUMERATE_SESSIONS, + COULD_NOT_OPEN_PIPE, + STDERR_COULD_NOT_BE_READ, +}; + +get_num_of_users_wrapper get_num_of_users_systemd(); +get_num_of_users_wrapper get_num_of_users_utmp(); +get_num_of_users_wrapper get_num_of_users_windows(); +get_num_of_users_wrapper get_num_of_users_who_command(); -- cgit v1.2.3-74-g34f1 From ee3c903fbe37cc5c7537921cdb31e39b4ea25bc6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:06:54 +0100 Subject: users.c: Include default definitions --- plugins/check_users.d/users.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c index 7969ae79..95c05d6f 100644 --- a/plugins/check_users.d/users.c +++ b/plugins/check_users.d/users.c @@ -50,6 +50,7 @@ get_num_of_users_wrapper get_num_of_users_windows() { #else // _WIN32 # include "../../config.h" +# include # ifdef HAVE_LIBSYSTEMD # include -- cgit v1.2.3-74-g34f1 From 5a79cd31c48307d18a637271f4a0a13327fd3073 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:15:57 +0100 Subject: Reintroduce positional arguments --- plugins/check_users.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/plugins/check_users.c b/plugins/check_users.c index 61427f97..b7b7bd24 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -200,27 +200,42 @@ check_users_config_wrapper process_arguments(int argc, char **argv) { } } + int option_char = optind; + + if (warning_range == NULL && argc > option_char) { + warning_range = argv[option_char++]; + } + + if (critical_range == NULL && argc > option_char) { + critical_range = argv[option_char++]; + } + // TODO add proper verification for ranges here! + mp_range_parsed tmp; if (warning_range) { - mp_range_parsed tmp = mp_parse_range_string(warning_range); - if (tmp.error == MP_PARSING_SUCCES) { - result.config.thresholds.warning = tmp.range; - result.config.thresholds.warning_is_set = true; - } else { - printf("Failed to parse warning range: %s", warning_range); - exit(STATE_UNKNOWN); - } + tmp = mp_parse_range_string(warning_range); + } else { + tmp = mp_parse_range_string(argv[option_char++]); + } + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.warning = tmp.range; + result.config.thresholds.warning_is_set = true; + } else { + printf("Failed to parse warning range: %s", warning_range); + exit(STATE_UNKNOWN); } if (critical_range) { - mp_range_parsed tmp = mp_parse_range_string(critical_range); - if (tmp.error == MP_PARSING_SUCCES) { - result.config.thresholds.critical = tmp.range; - result.config.thresholds.critical_is_set = true; - } else { - printf("Failed to parse critical range: %s", critical_range); - exit(STATE_UNKNOWN); - } + tmp = mp_parse_range_string(critical_range); + } else { + tmp = mp_parse_range_string(argv[option_char++]); + } + if (tmp.error == MP_PARSING_SUCCES) { + result.config.thresholds.critical = tmp.range; + result.config.thresholds.critical_is_set = true; + } else { + printf("Failed to parse critical range: %s", critical_range); + exit(STATE_UNKNOWN); } return result; -- cgit v1.2.3-74-g34f1 From 270d643d1155afd83a5c699c2e6721a4dea2c5ae Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:16:57 +0100 Subject: Adapt tests --- plugins/t/check_users.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/t/check_users.t b/plugins/t/check_users.t index 21c3e53d..446e0476 100644 --- a/plugins/t/check_users.t +++ b/plugins/t/check_users.t @@ -15,8 +15,8 @@ use NPTest; use vars qw($tests); BEGIN {$tests = 12; plan tests => $tests} -my $successOutput = '/^USERS OK - [0-9]+ users currently logged in/'; -my $failureOutput = '/^USERS CRITICAL - [0-9]+ users currently logged in/'; +my $successOutput = '/[0-9]+ users currently logged in/'; +my $failureOutput = '/[0-9]+ users currently logged in/'; my $wrongOptionOutput = '/Usage:/'; my $t; -- cgit v1.2.3-74-g34f1 From 1f7e8b57e2105e68ba97c9a5c9fbafa6b692a804 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 22:18:09 +0100 Subject: check_users: ignore temporary files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f9cb37e4..5632b016 100644 --- a/.gitignore +++ b/.gitignore @@ -197,6 +197,8 @@ NP-VERSION-FILE /plugins/check_udp /plugins/check_ups /plugins/check_users +/plugins/check_users.d/.deps +/plugins/check_users.d/.dirstamp /plugins/check_vsz /plugins/config.h /plugins/config.h.in -- cgit v1.2.3-74-g34f1 From 4a0e309f9f5a85af6087e3eb7034a98dd410c6f7 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 12 Mar 2025 23:37:36 +0100 Subject: Fail correctly with missing thresholds --- plugins/check_users.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/check_users.c b/plugins/check_users.c index b7b7bd24..cd3bd181 100644 --- a/plugins/check_users.c +++ b/plugins/check_users.c @@ -215,8 +215,11 @@ check_users_config_wrapper process_arguments(int argc, char **argv) { if (warning_range) { tmp = mp_parse_range_string(warning_range); } else { - tmp = mp_parse_range_string(argv[option_char++]); - } + printf("Warning threshold missing\n"); + print_usage(); + exit(STATE_UNKNOWN); + } + if (tmp.error == MP_PARSING_SUCCES) { result.config.thresholds.warning = tmp.range; result.config.thresholds.warning_is_set = true; @@ -228,8 +231,11 @@ check_users_config_wrapper process_arguments(int argc, char **argv) { if (critical_range) { tmp = mp_parse_range_string(critical_range); } else { - tmp = mp_parse_range_string(argv[option_char++]); + printf("Critical threshold missing\n"); + print_usage(); + exit(STATE_UNKNOWN); } + if (tmp.error == MP_PARSING_SUCCES) { result.config.thresholds.critical = tmp.range; result.config.thresholds.critical_is_set = true; -- cgit v1.2.3-74-g34f1 From f2900e0ccf18f321857e4072681ad9c10a0cb67f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:36:36 +0100 Subject: check_load: clang-format --- plugins/check_load.c | 330 +++++++++++++++++++++++++-------------------------- 1 file changed, 162 insertions(+), 168 deletions(-) diff --git a/plugins/check_load.c b/plugins/check_load.c index 1431d130..e3a45f58 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -1,32 +1,32 @@ /***************************************************************************** -* -* Monitoring check_load plugin -* -* License: GPL -* Copyright (c) 1999-2007 Monitoring Plugins Development Team -* -* Description: -* -* This file contains the check_load plugin -* -* This plugin tests the current system load average. -* -* -* 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_load plugin + * + * License: GPL + * Copyright (c) 1999-2007 Monitoring Plugins Development Team + * + * Description: + * + * This file contains the check_load plugin + * + * This plugin tests the current system load average. + * + * + * 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 . + * + * + *****************************************************************************/ const char *progname = "check_load"; const char *copyright = "1999-2022"; @@ -40,127 +40,129 @@ const char *email = "devel@monitoring-plugins.org"; #include #ifdef HAVE_SYS_LOADAVG_H -#include +# include #endif /* needed for compilation under NetBSD, as suggested by Andy Doran */ #ifndef LOADAVG_1MIN -#define LOADAVG_1MIN 0 -#define LOADAVG_5MIN 1 -#define LOADAVG_15MIN 2 +# define LOADAVG_1MIN 0 +# define LOADAVG_5MIN 1 +# define LOADAVG_15MIN 2 #endif /* !defined LOADAVG_1MIN */ - -static int process_arguments (int argc, char **argv); -static int validate_arguments (void); -void print_help (void); -void print_usage (void); +static int process_arguments(int argc, char **argv); +static int validate_arguments(void); +void print_help(void); +void print_usage(void); static int print_top_consuming_processes(); static int n_procs_to_show = 0; /* strictly for pretty-print usage in loops */ -static const int nums[3] = { 1, 5, 15 }; +static const int nums[3] = {1, 5, 15}; /* provide some fairly sane defaults */ -double wload[3] = { 0.0, 0.0, 0.0 }; -double cload[3] = { 0.0, 0.0, 0.0 }; -#define la1 la[0] -#define la5 la[1] +double wload[3] = {0.0, 0.0, 0.0}; +double cload[3] = {0.0, 0.0, 0.0}; +#define la1 la[0] +#define la5 la[1] #define la15 la[2] char *status_line; bool take_into_account_cpus = false; -static void -get_threshold(char *arg, double *th) -{ +static void get_threshold(char *arg, double *th) { size_t i, n; int valid = 0; char *str = arg, *p; n = strlen(arg); - for(i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) { th[i] = strtod(str, &p); - if(p == str) break; + if (p == str) { + break; + } valid = 1; str = p + 1; - if(n <= (size_t)(str - arg)) break; + if (n <= (size_t)(str - arg)) { + break; + } } /* empty argument or non-floatish, so warn about it and die */ - if(!i && !valid) usage (_("Warning threshold must be float or float triplet!\n")); + if (!i && !valid) { + usage(_("Warning threshold must be float or float triplet!\n")); + } - if(i != 2) { + if (i != 2) { /* one or more numbers were given, so fill array with last * we got (most likely to NOT produce the least expected result) */ - for(n = i; n < 3; n++) th[n] = th[i]; + for (n = i; n < 3; n++) { + th[n] = th[i]; + } } } - -int -main (int argc, char **argv) -{ +int main(int argc, char **argv) { int result = -1; int i; long numcpus; - double la[3] = { 0.0, 0.0, 0.0 }; /* NetBSD complains about uninitialized arrays */ + double la[3] = {0.0, 0.0, 0.0}; /* NetBSD complains about uninitialized arrays */ #ifndef HAVE_GETLOADAVG char input_buffer[MAX_INPUT_BUFFER]; #endif - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); setlocale(LC_NUMERIC, "POSIX"); /* 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")); + if (process_arguments(argc, argv) == ERROR) { + usage4(_("Could not parse arguments")); + } #ifdef HAVE_GETLOADAVG - result = getloadavg (la, 3); - if (result != 3) + result = getloadavg(la, 3); + if (result != 3) { return STATE_UNKNOWN; + } #else - child_process = spopen (PATH_TO_UPTIME); + child_process = spopen(PATH_TO_UPTIME); if (child_process == NULL) { - printf (_("Error opening %s\n"), PATH_TO_UPTIME); + printf(_("Error opening %s\n"), PATH_TO_UPTIME); return STATE_UNKNOWN; } - child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r"); + child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); if (child_stderr == NULL) { - printf (_("Could not open stderr for %s\n"), PATH_TO_UPTIME); + printf(_("Could not open stderr for %s\n"), PATH_TO_UPTIME); } - fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process); - if(strstr(input_buffer, "load average:")) { - sscanf (input_buffer, "%*[^l]load average: %lf, %lf, %lf", &la1, &la5, &la15); - } - else if(strstr(input_buffer, "load averages:")) { - sscanf (input_buffer, "%*[^l]load averages: %lf, %lf, %lf", &la1, &la5, &la15); - } - else { - printf (_("could not parse load from uptime %s: %d\n"), PATH_TO_UPTIME, result); + fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); + if (strstr(input_buffer, "load average:")) { + sscanf(input_buffer, "%*[^l]load average: %lf, %lf, %lf", &la1, &la5, &la15); + } else if (strstr(input_buffer, "load averages:")) { + sscanf(input_buffer, "%*[^l]load averages: %lf, %lf, %lf", &la1, &la5, &la15); + } else { + printf(_("could not parse load from uptime %s: %d\n"), PATH_TO_UPTIME, result); return STATE_UNKNOWN; - } + } - result = spclose (child_process); + result = spclose(child_process); if (result) { - printf (_("Error code %d returned in %s\n"), result, PATH_TO_UPTIME); + printf(_("Error code %d returned in %s\n"), result, PATH_TO_UPTIME); return STATE_UNKNOWN; } #endif if ((la[0] < 0.0) || (la[1] < 0.0) || (la[2] < 0.0)) { #ifdef HAVE_GETLOADAVG - printf (_("Error in getloadavg()\n")); + printf(_("Error in getloadavg()\n")); #else - printf (_("Error processing %s\n"), PATH_TO_UPTIME); + printf(_("Error processing %s\n"), PATH_TO_UPTIME); #endif return STATE_UNKNOWN; } @@ -171,8 +173,7 @@ main (int argc, char **argv) xasprintf(&status_line, _("load average: %.2f, %.2f, %.2f"), la1, la5, la15); xasprintf(&status_line, ("total %s"), status_line); - - double scaled_la[3] = { 0.0, 0.0, 0.0 }; + double scaled_la[3] = {0.0, 0.0, 0.0}; bool is_using_scaled_load_values = false; if (take_into_account_cpus == true && (numcpus = GET_NUMBER_OF_CPUS()) > 0) { @@ -187,24 +188,26 @@ main (int argc, char **argv) xasprintf(&status_line, "scaled %s - %s", tmp, status_line); } - for(i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) { if (is_using_scaled_load_values) { - if(scaled_la[i] > cload[i]) { + if (scaled_la[i] > cload[i]) { result = STATE_CRITICAL; break; + } else if (scaled_la[i] > wload[i]) { + result = STATE_WARNING; } - else if(scaled_la[i] > wload[i]) result = STATE_WARNING; } else { - if(la[i] > cload[i]) { + if (la[i] > cload[i]) { result = STATE_CRITICAL; break; + } else if (la[i] > wload[i]) { + result = STATE_WARNING; } - else if(la[i] > wload[i]) result = STATE_WARNING; } } printf("LOAD %s - %s|", state_text(result), status_line); - for(i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) { if (is_using_scaled_load_values) { printf("load%d=%.3f;;;0; ", nums[i], la[i]); printf("scaled_load%d=%.3f;%.3f;%.3f;0; ", nums[i], scaled_la[i], wload[i], cload[i]); @@ -220,32 +223,29 @@ main (int argc, char **argv) return result; } - /* process command-line arguments */ -static int -process_arguments (int argc, char **argv) -{ +static int process_arguments(int argc, char **argv) { int c = 0; int option = 0; - static struct option longopts[] = { - {"warning", required_argument, 0, 'w'}, - {"critical", required_argument, 0, 'c'}, - {"percpu", no_argument, 0, 'r'}, - {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, - {"procs-to-show", required_argument, 0, 'n'}, - {0, 0, 0, 0} - }; - - if (argc < 2) + static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, + {"percpu", no_argument, 0, 'r'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"procs-to-show", required_argument, 0, 'n'}, + {0, 0, 0, 0}}; + + if (argc < 2) { return ERROR; + } while (1) { - c = getopt_long (argc, argv, "Vhrc:w:n:", longopts, &option); + c = getopt_long(argc, argv, "Vhrc:w:n:", longopts, &option); - if (c == -1 || c == EOF) + if (c == -1 || c == EOF) { break; + } switch (c) { case 'w': /* warning time threshold */ @@ -257,94 +257,89 @@ process_arguments (int argc, char **argv) case 'r': /* Divide load average by number of CPUs */ take_into_account_cpus = true; break; - case 'V': /* version */ - print_revision (progname, NP_VERSION); - exit (STATE_UNKNOWN); - case 'h': /* help */ - print_help (); - exit (STATE_UNKNOWN); + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + case 'h': /* help */ + print_help(); + exit(STATE_UNKNOWN); case 'n': n_procs_to_show = atoi(optarg); break; - case '?': /* help */ - usage5 (); + case '?': /* help */ + usage5(); } } c = optind; - if (c == argc) - return validate_arguments (); + if (c == argc) { + return validate_arguments(); + } /* handle the case if both arguments are missing, * but not if only one is given without -c or -w flag */ - if(c - argc == 2) { + if (c - argc == 2) { get_threshold(argv[c++], wload); get_threshold(argv[c++], cload); - } - else if(c - argc == 1) { + } else if (c - argc == 1) { get_threshold(argv[c++], cload); } - return validate_arguments (); + return validate_arguments(); } - -static int -validate_arguments (void) -{ +static int validate_arguments(void) { int i = 0; /* match cload first, as it will give the most friendly error message * if user hasn't given the -c switch properly */ - for(i = 0; i < 3; i++) { - if(cload[i] < 0) - die (STATE_UNKNOWN, _("Critical threshold for %d-minute load average is not specified\n"), nums[i]); - if(wload[i] < 0) - die (STATE_UNKNOWN, _("Warning threshold for %d-minute load average is not specified\n"), nums[i]); - if(wload[i] > cload[i]) - die (STATE_UNKNOWN, _("Parameter inconsistency: %d-minute \"warning load\" is greater than \"critical load\"\n"), nums[i]); + for (i = 0; i < 3; i++) { + if (cload[i] < 0) { + die(STATE_UNKNOWN, _("Critical threshold for %d-minute load average is not specified\n"), nums[i]); + } + if (wload[i] < 0) { + die(STATE_UNKNOWN, _("Warning threshold for %d-minute load average is not specified\n"), nums[i]); + } + if (wload[i] > cload[i]) { + die(STATE_UNKNOWN, _("Parameter inconsistency: %d-minute \"warning load\" is greater than \"critical load\"\n"), nums[i]); + } } return OK; } +void print_help(void) { + print_revision(progname, NP_VERSION); -void -print_help (void) -{ - print_revision (progname, NP_VERSION); - - printf ("Copyright (c) 1999 Felipe Gustavo de Almeida \n"); - printf (COPYRIGHT, copyright, email); + printf("Copyright (c) 1999 Felipe Gustavo de Almeida \n"); + printf(COPYRIGHT, copyright, email); - printf (_("This plugin tests the current system load average.")); + printf(_("This plugin tests the current system load average.")); - printf ("\n\n"); + printf("\n\n"); - print_usage (); + print_usage(); - printf (UT_HELP_VRSN); - printf (UT_EXTRA_OPTS); + printf(UT_HELP_VRSN); + printf(UT_EXTRA_OPTS); - printf (" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15"); - printf (" %s\n", _("Exit with WARNING status if load average exceeds WLOADn")); - printf (" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15"); - printf (" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn")); - printf (" %s\n", _("the load average format is the same used by \"uptime\" and \"w\"")); - printf (" %s\n", "-r, --percpu"); - printf (" %s\n", _("Divide the load averages by the number of CPUs (when possible)")); - printf (" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS"); - printf (" %s\n", _("Number of processes to show when printing the top consuming processes.")); - printf (" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0")); + printf(" %s\n", "-w, --warning=WLOAD1,WLOAD5,WLOAD15"); + printf(" %s\n", _("Exit with WARNING status if load average exceeds WLOADn")); + printf(" %s\n", "-c, --critical=CLOAD1,CLOAD5,CLOAD15"); + printf(" %s\n", _("Exit with CRITICAL status if load average exceed CLOADn")); + printf(" %s\n", _("the load average format is the same used by \"uptime\" and \"w\"")); + printf(" %s\n", "-r, --percpu"); + printf(" %s\n", _("Divide the load averages by the number of CPUs (when possible)")); + printf(" %s\n", "-n, --procs-to-show=NUMBER_OF_PROCS"); + printf(" %s\n", _("Number of processes to show when printing the top consuming processes.")); + printf(" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0")); - printf (UT_SUPPORT); + printf(UT_SUPPORT); } -void -print_usage (void) -{ - printf ("%s\n", _("Usage:")); - printf ("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname); +void print_usage(void) { + printf("%s\n", _("Usage:")); + printf("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname); } #ifdef PS_USES_PROCPCPU @@ -356,14 +351,14 @@ int cmpstringp(const void *p1, const void *p2) { int procrss = 0; float procpcpu = 0; char procstat[8]; -#ifdef PS_USES_PROCETIME +# ifdef PS_USES_PROCETIME char procetime[MAX_INPUT_BUFFER]; -#endif /* PS_USES_PROCETIME */ +# endif /* PS_USES_PROCETIME */ char procprog[MAX_INPUT_BUFFER]; int pos; - sscanf (* (char * const *) p1, PS_FORMAT, PS_VARLIST); + sscanf(*(char *const *)p1, PS_FORMAT, PS_VARLIST); float procpcpu1 = procpcpu; - sscanf (* (char * const *) p2, PS_FORMAT, PS_VARLIST); + sscanf(*(char *const *)p2, PS_FORMAT, PS_VARLIST); return procpcpu1 < procpcpu; } #endif /* PS_USES_PROCPCPU */ @@ -371,7 +366,7 @@ int cmpstringp(const void *p1, const void *p2) { static int print_top_consuming_processes() { int i = 0; struct output chld_out, chld_err; - if(np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0){ + if (np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0) { fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND); return STATE_UNKNOWN; } @@ -380,10 +375,9 @@ static int print_top_consuming_processes() { return STATE_UNKNOWN; } #ifdef PS_USES_PROCPCPU - qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char*), cmpstringp); + qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp); #endif /* PS_USES_PROCPCPU */ - int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) - ? (int)chld_out.lines : n_procs_to_show + 1; + int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) ? (int)chld_out.lines : n_procs_to_show + 1; for (i = 0; i < lines_to_show; i += 1) { printf("%s\n", chld_out.line[i]); } -- cgit v1.2.3-74-g34f1 From 08a475a14fff2d0eee2e49f4765cb286cac8d2e2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 13 Mar 2025 23:41:12 +0100 Subject: Refactor check_load + new ouput --- plugins/Makefile.am | 1 + plugins/check_load.c | 420 ++++++++++++++++++++++++------------------ plugins/check_load.d/config.h | 30 +++ 3 files changed, 274 insertions(+), 177 deletions(-) create mode 100644 plugins/check_load.d/config.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 30ca63d1..f395e594 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -56,6 +56,7 @@ EXTRA_DIST = t \ check_game.d \ check_radius.d \ check_time.d \ + check_load.d \ check_nagios.d \ check_dbi.d \ check_tcp.d \ diff --git a/plugins/check_load.c b/plugins/check_load.c index e3a45f58..57be8c69 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -28,6 +28,9 @@ * *****************************************************************************/ +#include "output.h" +#include "perfdata.h" +#include "thresholds.h" const char *progname = "check_load"; const char *copyright = "1999-2022"; const char *email = "devel@monitoring-plugins.org"; @@ -36,6 +39,10 @@ const char *email = "devel@monitoring-plugins.org"; #include "./runcmd.h" #include "./utils.h" #include "./popen.h" +#include "states.h" +#include "check_load.d/config.h" + +#include "../gl/stdlib.h" #include @@ -50,70 +57,68 @@ const char *email = "devel@monitoring-plugins.org"; # define LOADAVG_15MIN 2 #endif /* !defined LOADAVG_1MIN */ -static int process_arguments(int argc, char **argv); -static int validate_arguments(void); +typedef struct { + int errorcode; + check_load_config config; +} check_load_config_wrapper; +static check_load_config_wrapper process_arguments(int argc, char **argv); +static check_load_config_wrapper validate_arguments(check_load_config_wrapper /*config_wrapper*/); + void print_help(void); void print_usage(void); -static int print_top_consuming_processes(); - -static int n_procs_to_show = 0; - -/* strictly for pretty-print usage in loops */ -static const int nums[3] = {1, 5, 15}; - -/* provide some fairly sane defaults */ -double wload[3] = {0.0, 0.0, 0.0}; -double cload[3] = {0.0, 0.0, 0.0}; -#define la1 la[0] -#define la5 la[1] -#define la15 la[2] - -char *status_line; -bool take_into_account_cpus = false; - -static void get_threshold(char *arg, double *th) { - size_t i, n; - int valid = 0; - char *str = arg, *p; - - n = strlen(arg); - for (i = 0; i < 3; i++) { - th[i] = strtod(str, &p); - if (p == str) { +typedef struct { + int errorcode; + char **top_processes; +} top_processes_result; +static top_processes_result print_top_consuming_processes(int /*n_procs_to_show*/); + +typedef struct { + mp_range load[3]; +} parsed_thresholds; +static parsed_thresholds get_threshold(char *arg) { + size_t index; + char *str = arg; + char *tmp_pointer; + bool valid = false; + + parsed_thresholds result = { + .load[0] = mp_range_init(), + .load[1] = mp_range_init(), + .load[2] = mp_range_init(), + }; + + size_t arg_length = strlen(arg); + for (index = 0; index < 3; index++) { + double tmp = strtod(str, &tmp_pointer); + if (tmp_pointer == str) { break; } - valid = 1; - str = p + 1; - if (n <= (size_t)(str - arg)) { + result.load[index] = mp_range_set_end(result.load[index], mp_create_pd_value(tmp)); + + valid = true; + str = tmp_pointer + 1; + if (arg_length <= (size_t)(str - arg)) { break; } } /* empty argument or non-floatish, so warn about it and die */ - if (!i && !valid) { + if (!index && !valid) { usage(_("Warning threshold must be float or float triplet!\n")); } - if (i != 2) { + if (index != 2) { /* one or more numbers were given, so fill array with last * we got (most likely to NOT produce the least expected result) */ - for (n = i; n < 3; n++) { - th[n] = th[i]; + for (size_t tmp_index = index; tmp_index < 3; tmp_index++) { + result.load[tmp_index] = result.load[index]; } } + return result; } int main(int argc, char **argv) { - int result = -1; - int i; - long numcpus; - - double la[3] = {0.0, 0.0, 0.0}; /* NetBSD complains about uninitialized arrays */ -#ifndef HAVE_GETLOADAVG - char input_buffer[MAX_INPUT_BUFFER]; -#endif - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -122,112 +127,138 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) { + check_load_config_wrapper tmp_config = process_arguments(argc, argv); + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } -#ifdef HAVE_GETLOADAVG - result = getloadavg(la, 3); - if (result != 3) { - return STATE_UNKNOWN; - } -#else - child_process = spopen(PATH_TO_UPTIME); - if (child_process == NULL) { - printf(_("Error opening %s\n"), PATH_TO_UPTIME); - return STATE_UNKNOWN; - } - child_stderr = fdopen(child_stderr_array[fileno(child_process)], "r"); - if (child_stderr == NULL) { - printf(_("Could not open stderr for %s\n"), PATH_TO_UPTIME); - } - fgets(input_buffer, MAX_INPUT_BUFFER - 1, child_process); - if (strstr(input_buffer, "load average:")) { - sscanf(input_buffer, "%*[^l]load average: %lf, %lf, %lf", &la1, &la5, &la15); - } else if (strstr(input_buffer, "load averages:")) { - sscanf(input_buffer, "%*[^l]load averages: %lf, %lf, %lf", &la1, &la5, &la15); - } else { - printf(_("could not parse load from uptime %s: %d\n"), PATH_TO_UPTIME, result); - return STATE_UNKNOWN; - } + const check_load_config config = tmp_config.config; - result = spclose(child_process); - if (result) { - printf(_("Error code %d returned in %s\n"), result, PATH_TO_UPTIME); - return STATE_UNKNOWN; - } -#endif + double load_values[3] = {0, 0, 0}; - if ((la[0] < 0.0) || (la[1] < 0.0) || (la[2] < 0.0)) { -#ifdef HAVE_GETLOADAVG - printf(_("Error in getloadavg()\n")); -#else - printf(_("Error processing %s\n"), PATH_TO_UPTIME); -#endif - return STATE_UNKNOWN; + int error = getloadavg(load_values, 3); + if (error != 3) { + die(STATE_UNKNOWN, _("Failed to retrieve load values")); } - /* we got this far, so assume OK until we've measured */ - result = STATE_OK; - - xasprintf(&status_line, _("load average: %.2f, %.2f, %.2f"), la1, la5, la15); - xasprintf(&status_line, ("total %s"), status_line); + mp_check overall = mp_check_init(); + if (config.output_format_set) { + mp_set_format(config.output_format); + } - double scaled_la[3] = {0.0, 0.0, 0.0}; bool is_using_scaled_load_values = false; - - if (take_into_account_cpus == true && (numcpus = GET_NUMBER_OF_CPUS()) > 0) { + long numcpus; + if (config.take_into_account_cpus && ((numcpus = GET_NUMBER_OF_CPUS()) > 0)) { is_using_scaled_load_values = true; - scaled_la[0] = la[0] / numcpus; - scaled_la[1] = la[1] / numcpus; - scaled_la[2] = la[2] / numcpus; + double scaled_la[3] = {0.0, 0.0, 0.0}; + scaled_la[0] = load_values[0] / numcpus; + scaled_la[1] = load_values[1] / numcpus; + scaled_la[2] = load_values[2] / numcpus; + + mp_subcheck scaled_load_sc = mp_subcheck_init(); + scaled_load_sc = mp_set_subcheck_default_state(scaled_load_sc, STATE_OK); + scaled_load_sc.output = "Scaled Load (divided by number of CPUs"; + + mp_perfdata pd_scaled_load1 = perfdata_init(); + pd_scaled_load1.label = "scaled_load1"; + pd_scaled_load1 = mp_set_pd_value(pd_scaled_load1, scaled_la[0]); + pd_scaled_load1 = mp_pd_set_thresholds(pd_scaled_load1, config.th_load[0]); + + mp_subcheck scaled_load_sc1 = mp_subcheck_init(); + scaled_load_sc1 = mp_set_subcheck_state(scaled_load_sc1, mp_get_pd_status(pd_scaled_load1)); + mp_add_perfdata_to_subcheck(&scaled_load_sc1, pd_scaled_load1); + xasprintf(&scaled_load_sc1.output, "1 Minute: %s", pd_value_to_string(pd_scaled_load1.value)); + mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc1); + + mp_perfdata pd_scaled_load5 = perfdata_init(); + pd_scaled_load5.label = "scaled_load5"; + pd_scaled_load5 = mp_set_pd_value(pd_scaled_load5, scaled_la[1]); + pd_scaled_load5 = mp_pd_set_thresholds(pd_scaled_load5, config.th_load[1]); + + mp_subcheck scaled_load_sc5 = mp_subcheck_init(); + scaled_load_sc5 = mp_set_subcheck_state(scaled_load_sc5, mp_get_pd_status(pd_scaled_load5)); + mp_add_perfdata_to_subcheck(&scaled_load_sc5, pd_scaled_load5); + xasprintf(&scaled_load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_scaled_load5.value)); + mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc5); + + mp_perfdata pd_scaled_load15 = perfdata_init(); + pd_scaled_load15.label = "scaled_load15"; + pd_scaled_load15 = mp_set_pd_value(pd_scaled_load15, scaled_la[2]); + pd_scaled_load15 = mp_pd_set_thresholds(pd_scaled_load15, config.th_load[2]); + + mp_subcheck scaled_load_sc15 = mp_subcheck_init(); + scaled_load_sc15 = mp_set_subcheck_state(scaled_load_sc15, mp_get_pd_status(pd_scaled_load15)); + mp_add_perfdata_to_subcheck(&scaled_load_sc15, pd_scaled_load15); + xasprintf(&scaled_load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_scaled_load15.value)); + mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc15); + + mp_add_subcheck_to_check(&overall, scaled_load_sc); + } + + mp_subcheck load_sc = mp_subcheck_init(); + load_sc = mp_set_subcheck_default_state(load_sc, STATE_OK); + load_sc.output = "Total Load"; - char *tmp = NULL; - xasprintf(&tmp, _("load average: %.2f, %.2f, %.2f"), scaled_la[0], scaled_la[1], scaled_la[2]); - xasprintf(&status_line, "scaled %s - %s", tmp, status_line); + mp_perfdata pd_load1 = perfdata_init(); + pd_load1.label = "load1"; + pd_load1 = mp_set_pd_value(pd_load1, load_values[0]); + if (!is_using_scaled_load_values) { + pd_load1 = mp_pd_set_thresholds(pd_load1, config.th_load[0]); } - for (i = 0; i < 3; i++) { - if (is_using_scaled_load_values) { - if (scaled_la[i] > cload[i]) { - result = STATE_CRITICAL; - break; - } else if (scaled_la[i] > wload[i]) { - result = STATE_WARNING; - } - } else { - if (la[i] > cload[i]) { - result = STATE_CRITICAL; - break; - } else if (la[i] > wload[i]) { - result = STATE_WARNING; - } - } + mp_subcheck load_sc1 = mp_subcheck_init(); + load_sc1 = mp_set_subcheck_state(load_sc1, mp_get_pd_status(pd_load1)); + mp_add_perfdata_to_subcheck(&load_sc1, pd_load1); + xasprintf(&load_sc1.output, "1 Minute: %s", pd_value_to_string(pd_load1.value)); + mp_add_subcheck_to_subcheck(&load_sc, load_sc1); + + mp_perfdata pd_load5 = perfdata_init(); + pd_load5.label = "load5"; + pd_load5 = mp_set_pd_value(pd_load5, load_values[1]); + if (!is_using_scaled_load_values) { + pd_load5 = mp_pd_set_thresholds(pd_load5, config.th_load[1]); } - printf("LOAD %s - %s|", state_text(result), status_line); - for (i = 0; i < 3; i++) { - if (is_using_scaled_load_values) { - printf("load%d=%.3f;;;0; ", nums[i], la[i]); - printf("scaled_load%d=%.3f;%.3f;%.3f;0; ", nums[i], scaled_la[i], wload[i], cload[i]); - } else { - printf("load%d=%.3f;%.3f;%.3f;0; ", nums[i], la[i], wload[i], cload[i]); - } + mp_subcheck load_sc5 = mp_subcheck_init(); + load_sc5 = mp_set_subcheck_state(load_sc5, mp_get_pd_status(pd_load5)); + mp_add_perfdata_to_subcheck(&load_sc5, pd_load5); + xasprintf(&load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_load5.value)); + mp_add_subcheck_to_subcheck(&load_sc, load_sc5); + + mp_perfdata pd_load15 = perfdata_init(); + pd_load15.label = "load15"; + pd_load15 = mp_set_pd_value(pd_load15, load_values[2]); + if (!is_using_scaled_load_values) { + pd_load15 = mp_pd_set_thresholds(pd_load15, config.th_load[2]); } - putchar('\n'); - if (n_procs_to_show > 0) { - print_top_consuming_processes(); + mp_subcheck load_sc15 = mp_subcheck_init(); + load_sc15 = mp_set_subcheck_state(load_sc15, mp_get_pd_status(pd_load15)); + mp_add_perfdata_to_subcheck(&load_sc15, pd_load15); + xasprintf(&load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_load15.value)); + mp_add_subcheck_to_subcheck(&load_sc, load_sc15); + + mp_add_subcheck_to_check(&overall, load_sc); + + if (config.n_procs_to_show > 0) { + mp_subcheck top_proc_sc = mp_subcheck_init(); + top_proc_sc = mp_set_subcheck_state(top_proc_sc, STATE_OK); + top_processes_result top_proc = print_top_consuming_processes(config.n_procs_to_show); + top_proc_sc.output = ""; + + if (top_proc.errorcode == OK) { + for (int i = 0; i < config.n_procs_to_show; i++) { + xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output, top_proc.top_processes[i]); + } + } } - return result; + + mp_exit(overall); } /* process command-line arguments */ -static int process_arguments(int argc, char **argv) { - int c = 0; - - int option = 0; +static check_load_config_wrapper process_arguments(int argc, char **argv) { static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"percpu", no_argument, 0, 'r'}, @@ -236,26 +267,45 @@ static int process_arguments(int argc, char **argv) { {"procs-to-show", required_argument, 0, 'n'}, {0, 0, 0, 0}}; + check_load_config_wrapper result = { + .errorcode = OK, + .config = check_load_config_init(), + }; + if (argc < 2) { - return ERROR; + result.errorcode = ERROR; + return result; } - while (1) { - c = getopt_long(argc, argv, "Vhrc:w:n:", longopts, &option); + while (true) { + int option = 0; + int option_index = getopt_long(argc, argv, "Vhrc:w:n:", longopts, &option); - if (c == -1 || c == EOF) { + if (option_index == -1 || option_index == EOF) { break; } - switch (c) { - case 'w': /* warning time threshold */ - get_threshold(optarg, wload); - break; - case 'c': /* critical time threshold */ - get_threshold(optarg, cload); - break; + switch (option_index) { + case 'w': /* warning time threshold */ { + parsed_thresholds warning_range = get_threshold(optarg); + result.config.th_load[0].warning = warning_range.load[0]; + result.config.th_load[0].warning_is_set = true; + result.config.th_load[1].warning = warning_range.load[1]; + result.config.th_load[1].warning_is_set = true; + result.config.th_load[2].warning = warning_range.load[2]; + result.config.th_load[2].warning_is_set = true; + } break; + case 'c': /* critical time threshold */ { + parsed_thresholds critical_range = get_threshold(optarg); + result.config.th_load[0].critical = critical_range.load[0]; + result.config.th_load[0].critical_is_set = true; + result.config.th_load[1].critical = critical_range.load[1]; + result.config.th_load[1].critical_is_set = true; + result.config.th_load[2].critical = critical_range.load[2]; + result.config.th_load[2].critical_is_set = true; + } break; case 'r': /* Divide load average by number of CPUs */ - take_into_account_cpus = true; + result.config.take_into_account_cpus = true; break; case 'V': /* version */ print_revision(progname, NP_VERSION); @@ -264,49 +314,49 @@ static int process_arguments(int argc, char **argv) { print_help(); exit(STATE_UNKNOWN); case 'n': - n_procs_to_show = atoi(optarg); + result.config.n_procs_to_show = atoi(optarg); break; case '?': /* help */ usage5(); } } - c = optind; - if (c == argc) { - return validate_arguments(); + int index = optind; + if (index == argc) { + return validate_arguments(result); } /* handle the case if both arguments are missing, * but not if only one is given without -c or -w flag */ - if (c - argc == 2) { - get_threshold(argv[c++], wload); - get_threshold(argv[c++], cload); - } else if (c - argc == 1) { - get_threshold(argv[c++], cload); + if (index - argc == 2) { + parsed_thresholds warning_range = get_threshold(argv[index++]); + result.config.th_load[0].warning = warning_range.load[0]; + result.config.th_load[0].warning_is_set = true; + result.config.th_load[1].warning = warning_range.load[1]; + result.config.th_load[1].warning_is_set = true; + result.config.th_load[2].warning = warning_range.load[2]; + result.config.th_load[2].warning_is_set = true; + parsed_thresholds critical_range = get_threshold(argv[index++]); + result.config.th_load[0].critical = critical_range.load[0]; + result.config.th_load[0].critical_is_set = true; + result.config.th_load[1].critical = critical_range.load[1]; + result.config.th_load[1].critical_is_set = true; + result.config.th_load[2].critical = critical_range.load[2]; + result.config.th_load[2].critical_is_set = true; + } else if (index - argc == 1) { + parsed_thresholds critical_range = get_threshold(argv[index++]); + result.config.th_load[0].critical = critical_range.load[0]; + result.config.th_load[0].critical_is_set = true; + result.config.th_load[1].critical = critical_range.load[1]; + result.config.th_load[1].critical_is_set = true; + result.config.th_load[2].critical = critical_range.load[2]; + result.config.th_load[2].critical_is_set = true; } - return validate_arguments(); + return validate_arguments(result); } -static int validate_arguments(void) { - int i = 0; - - /* match cload first, as it will give the most friendly error message - * if user hasn't given the -c switch properly */ - for (i = 0; i < 3; i++) { - if (cload[i] < 0) { - die(STATE_UNKNOWN, _("Critical threshold for %d-minute load average is not specified\n"), nums[i]); - } - if (wload[i] < 0) { - die(STATE_UNKNOWN, _("Warning threshold for %d-minute load average is not specified\n"), nums[i]); - } - if (wload[i] > cload[i]) { - die(STATE_UNKNOWN, _("Parameter inconsistency: %d-minute \"warning load\" is greater than \"critical load\"\n"), nums[i]); - } - } - - return OK; -} +static check_load_config_wrapper validate_arguments(check_load_config_wrapper config_wrapper) { return config_wrapper; } void print_help(void) { print_revision(progname, NP_VERSION); @@ -363,23 +413,39 @@ int cmpstringp(const void *p1, const void *p2) { } #endif /* PS_USES_PROCPCPU */ -static int print_top_consuming_processes() { - int i = 0; - struct output chld_out, chld_err; +static top_processes_result print_top_consuming_processes(int n_procs_to_show) { + top_processes_result result = { + .errorcode = OK, + }; + struct output chld_out; + struct output chld_err; if (np_runcmd(PS_COMMAND, &chld_out, &chld_err, 0) != 0) { fprintf(stderr, _("'%s' exited with non-zero status.\n"), PS_COMMAND); - return STATE_UNKNOWN; + result.errorcode = ERROR; + return result; } + if (chld_out.lines < 2) { fprintf(stderr, _("some error occurred getting procs list.\n")); - return STATE_UNKNOWN; + result.errorcode = ERROR; + return result; } + #ifdef PS_USES_PROCPCPU qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp); #endif /* PS_USES_PROCPCPU */ int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) ? (int)chld_out.lines : n_procs_to_show + 1; - for (i = 0; i < lines_to_show; i += 1) { - printf("%s\n", chld_out.line[i]); + + result.top_processes = calloc(lines_to_show, sizeof(char *)); + if (result.top_processes == NULL) { + // Failed allocation + result.errorcode = ERROR; + return result; } - return OK; + + for (int i = 0; i < lines_to_show; i += 1) { + xasprintf(&result.top_processes[i], "%s", chld_out.line[i]); + } + + return result; } diff --git a/plugins/check_load.d/config.h b/plugins/check_load.d/config.h new file mode 100644 index 00000000..d399c19c --- /dev/null +++ b/plugins/check_load.d/config.h @@ -0,0 +1,30 @@ +#pragma once + +#include "output.h" +#include "thresholds.h" +typedef struct { + mp_thresholds th_load[3]; + + bool take_into_account_cpus; + int n_procs_to_show; + + mp_output_format output_format; + bool output_format_set; +} check_load_config; + +check_load_config check_load_config_init() { + check_load_config tmp = { + .th_load = + { + mp_thresholds_init(), + mp_thresholds_init(), + mp_thresholds_init(), + }, + + .take_into_account_cpus = false, + .n_procs_to_show = 0, + + .output_format_set = false, + }; + return tmp; +} -- cgit v1.2.3-74-g34f1 From 205b97b3e2133b14fbaab98f81b5f5991cfca1e9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 13 Mar 2025 23:48:33 +0100 Subject: check_load: Remove output formatting test and adapt others --- plugins/t/check_load.t | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/t/check_load.t b/plugins/t/check_load.t index bba8947c..3e453703 100644 --- a/plugins/t/check_load.t +++ b/plugins/t/check_load.t @@ -16,28 +16,28 @@ my $successScaledOutput = "/^LOAD OK - scaled load average: $loadValue, $loadVal my $failureOutput = "/^LOAD CRITICAL - total load average: $loadValue, $loadValue, $loadValue/"; my $failurScaledOutput = "/^LOAD CRITICAL - scaled load average: $loadValue, $loadValue, $loadValue - total load average: $loadValue, $loadValue, $loadValue/"; -plan tests => 13; +plan tests => 8; $res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100" ); cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); -like( $res->output, $successOutput, "Output OK"); +# like( $res->output, $successOutput, "Output OK"); $res = NPTest->testCmd( "./check_load -w 0,0,0 -c 0,0,0" ); cmp_ok( $res->return_code, 'eq', 2, "Load over 0"); -like( $res->output, $failureOutput, "Output OK"); +# like( $res->output, $failureOutput, "Output OK"); $res = NPTest->testCmd( "./check_load -r -w 0,0,0 -c 0,0,0" ); cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division"); -like( $res->output, $failurScaledOutput, "Output OK"); +# like( $res->output, $failurScaledOutput, "Output OK"); $res = NPTest->testCmd( "./check_load -w 100 -c 100,110" ); cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments"); -like( $res->output, $successOutput, "Output OK"); -like( $res->perf_output, "/load1=$loadValue;100.000;100.000/", "Test handling of non triplet thresholds (load1)"); -like( $res->perf_output, "/load5=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load5)"); -like( $res->perf_output, "/load15=$loadValue;100.000;110.000/", "Test handling of non triplet thresholds (load15)"); +# like( $res->output, $successOutput, "Output OK"); +like( $res->perf_output, "/load1=$loadValue;~:100.0+;~:100.0+/", "Test handling of non triplet thresholds (load1)"); +like( $res->perf_output, "/load5=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load5)"); +like( $res->perf_output, "/load15=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load15)"); $res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" ); cmp_ok( $res->return_code, 'eq', 0, "load not over 100"); -like( $res->output, $successScaledOutput, "Output OK"); +# like( $res->output, $successScaledOutput, "Output OK"); -- cgit v1.2.3-74-g34f1 From fedff97c96ec7f7866fa15bd38b9a349cf260cad Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 13 Mar 2025 23:51:51 +0100 Subject: improved includes and some comments --- plugins/check_load.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/plugins/check_load.c b/plugins/check_load.c index 57be8c69..5d3e6237 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -28,28 +28,24 @@ * *****************************************************************************/ -#include "output.h" -#include "perfdata.h" -#include "thresholds.h" const char *progname = "check_load"; const char *copyright = "1999-2022"; const char *email = "devel@monitoring-plugins.org"; #include "./common.h" +#include #include "./runcmd.h" #include "./utils.h" #include "./popen.h" -#include "states.h" +#include "../lib/states.h" +#include "../lib/output.h" +#include "../lib/perfdata.h" +#include "../lib/thresholds.h" #include "check_load.d/config.h" +// getloadavg comes from gnulib #include "../gl/stdlib.h" -#include - -#ifdef HAVE_SYS_LOADAVG_H -# include -#endif - /* needed for compilation under NetBSD, as suggested by Andy Doran */ #ifndef LOADAVG_1MIN # define LOADAVG_1MIN 0 @@ -136,6 +132,7 @@ int main(int argc, char **argv) { double load_values[3] = {0, 0, 0}; + // this should be getloadavg from gnulib, should work everywhere™ int error = getloadavg(load_values, 3); if (error != 3) { die(STATE_UNKNOWN, _("Failed to retrieve load values")); -- cgit v1.2.3-74-g34f1 From 5eaeadbb3a50e254fb465bf22c6022de9ecbab2d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 14 Mar 2025 00:04:02 +0100 Subject: check_load: Actually allow output format configuring --- plugins/check_load.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/plugins/check_load.c b/plugins/check_load.c index 5d3e6237..bf3c89d8 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -256,12 +256,18 @@ int main(int argc, char **argv) { /* process command-line arguments */ static check_load_config_wrapper process_arguments(int argc, char **argv) { + + enum { + output_format_index = CHAR_MAX + 1, + }; + static struct option longopts[] = {{"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"percpu", no_argument, 0, 'r'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"procs-to-show", required_argument, 0, 'n'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_load_config_wrapper result = { @@ -283,6 +289,18 @@ static check_load_config_wrapper process_arguments(int argc, char **argv) { } switch (option_index) { + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_set = true; + result.config.output_format = parser.output_format; + break; + } case 'w': /* warning time threshold */ { parsed_thresholds warning_range = get_threshold(optarg); result.config.th_load[0].warning = warning_range.load[0]; @@ -381,6 +399,7 @@ void print_help(void) { printf(" %s\n", _("Number of processes to show when printing the top consuming processes.")); printf(" %s\n", _("NUMBER_OF_PROCS=0 disables this feature. Default value is 0")); + printf(UT_OUTPUT_FORMAT); printf(UT_SUPPORT); } -- cgit v1.2.3-74-g34f1 From d24316a6b41305f91346e0115ae1f9fe38bff2ce Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:40:29 +0100 Subject: check_disk: clang-format --- plugins/check_disk.c | 129 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 037a6f7a..c3534060 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -191,8 +191,9 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) + if (process_arguments(argc, argv) == ERROR) { usage4(_("Could not parse arguments")); + } /* If a list of paths has not been selected, find entire mount list and create list of paths @@ -245,12 +246,14 @@ int main(int argc, char **argv) { /* Process for every path in list */ for (path = path_select_list; path; path = path->name_next) { - if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) + if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) { printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, path->freespace_percent->critical->end); + } - if (verbose >= 3 && path->group != NULL) + if (verbose >= 3 && path->group != NULL) { printf("Group of %s: %s\n", path->name, path->group); + } /* reset disk result */ disk_result = STATE_UNKNOWN; @@ -262,11 +265,13 @@ int main(int argc, char **argv) { } #ifdef __CYGWIN__ - if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) + if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) { continue; + } snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); - if (GetDriveType(mountdir) != DRIVE_FIXED) + if (GetDriveType(mountdir) != DRIVE_FIXED) { me->me_remote = 1; + } #endif /* Filters */ @@ -327,33 +332,39 @@ int main(int argc, char **argv) { /* Threshold comparisons */ temp_result = get_status(path->dfree_units, path->freespace_units); - if (verbose >= 3) + if (verbose >= 3) { printf("Freespace_units result=%d\n", temp_result); + } disk_result = max_state(disk_result, temp_result); temp_result = get_status(path->dfree_pct, path->freespace_percent); - if (verbose >= 3) + if (verbose >= 3) { printf("Freespace%% result=%d\n", temp_result); + } disk_result = max_state(disk_result, temp_result); temp_result = get_status(path->dused_units, path->usedspace_units); - if (verbose >= 3) + if (verbose >= 3) { printf("Usedspace_units result=%d\n", temp_result); + } disk_result = max_state(disk_result, temp_result); temp_result = get_status(path->dused_pct, path->usedspace_percent); - if (verbose >= 3) + if (verbose >= 3) { printf("Usedspace_percent result=%d\n", temp_result); + } disk_result = max_state(disk_result, temp_result); temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent); - if (verbose >= 3) + if (verbose >= 3) { printf("Usedinodes_percent result=%d\n", temp_result); + } disk_result = max_state(disk_result, temp_result); temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent); - if (verbose >= 3) + if (verbose >= 3) { printf("Freeinodes_percent result=%d\n", temp_result); + } disk_result = max_state(disk_result, temp_result); result = max_state(result, disk_result); @@ -414,8 +425,9 @@ int main(int argc, char **argv) { true, path->inodes_total)); } - if (disk_result == STATE_OK && erronly && !verbose) + if (disk_result == STATE_OK && erronly && !verbose) { continue; + } if (disk_result && verbose >= 1) { xasprintf(&flag_header, " %s [", state_text(disk_result)); @@ -434,8 +446,9 @@ int main(int argc, char **argv) { } } - if (verbose >= 2) + if (verbose >= 2) { xasprintf(&output, "%s%s", output, details); + } if (strcmp(output, "") == 0 && !erronly) { preamble = ""; @@ -509,20 +522,24 @@ int process_arguments(int argc, char **argv) { {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; - if (argc < 2) + if (argc < 2) { return ERROR; + } np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); - for (c = 1; c < argc; c++) - if (strcmp("-to", argv[c]) == 0) + for (c = 1; c < argc; c++) { + if (strcmp("-to", argv[c]) == 0) { strcpy(argv[c], "-t"); + } + } while (1) { c = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); - if (c == -1 || c == EOF) + if (c == -1 || c == EOF) { break; + } switch (c) { case 't': /* timeout period */ @@ -594,8 +611,9 @@ int process_arguments(int argc, char **argv) { } break; case 'u': - if (units) + if (units) { free(units); + } if (!strcasecmp(optarg, "bytes")) { mult = (uintmax_t)1; units = strdup("B"); @@ -632,19 +650,22 @@ int process_arguments(int argc, char **argv) { } else { die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); } - if (units == NULL) + if (units == NULL) { die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units"); + } break; case 'k': /* display mountpoint */ mult = 1024; - if (units) + if (units) { free(units); + } units = strdup("kiB"); break; case 'm': /* display mountpoint */ mult = 1024 * 1024; - if (units) + if (units) { free(units); + } units = strdup("MiB"); break; case 'L': @@ -715,25 +736,28 @@ int process_arguments(int argc, char **argv) { erronly = true; break; case 'E': - if (path_selected) + if (path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); + } exact_match = true; break; case 'f': freespace_ignore_reserved = true; break; case 'g': - if (path_selected) + if (path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); + } group = optarg; break; case 'I': cflags |= REG_ICASE; // Intentional fallthrough case 'i': - if (!path_selected) + if (!path_selected) { die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); + } err = regcomp(&re, optarg, cflags); if (err != 0) { regerror(err, &re, errbuf, MAX_INPUT_BUFFER); @@ -747,13 +771,15 @@ int process_arguments(int argc, char **argv) { if (temp_list->best_match) { if (np_regex_match_mount_entry(temp_list->best_match, &re)) { - if (verbose >= 3) + if (verbose >= 3) { printf("ignoring %s matching regex\n", temp_list->name); + } temp_list = np_del_parameter(temp_list, previous); /* pointer to first element needs to be updated if first item gets deleted */ - if (previous == NULL) + if (previous == NULL) { path_select_list = temp_list; + } } else { previous = temp_list; temp_list = temp_list->name_next; @@ -793,8 +819,9 @@ int process_arguments(int argc, char **argv) { for (me = mount_list; me; me = me->me_next) { if (np_regex_match_mount_entry(me, &re)) { fnd = true; - if (verbose >= 3) + if (verbose >= 3) { printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); + } /* add parameter if not found. overwrite thresholds if path has already been added */ if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { @@ -810,8 +837,9 @@ int process_arguments(int argc, char **argv) { path_selected = true; break; } - if (!fnd) + if (!fnd) { die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); + } fnd = false; path_selected = true; @@ -827,8 +855,9 @@ int process_arguments(int argc, char **argv) { if (path_selected == false) { struct parameter_list *path; for (me = mount_list; me; me = me->me_next) { - if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) + if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { path = np_add_parameter(&path_select_list, me->me_mountdir); + } path->best_match = me; path->group = group; set_all_thresholds(path); @@ -863,11 +892,13 @@ int process_arguments(int argc, char **argv) { /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ c = optind; - if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) + if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) { warn_usedspace_percent = argv[c++]; + } - if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) + if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) { crit_usedspace_percent = argv[c++]; + } if (argc > c) { se = np_add_parameter(&path_select_list, strdup(argv[c++])); @@ -884,23 +915,29 @@ int process_arguments(int argc, char **argv) { } void set_all_thresholds(struct parameter_list *path) { - if (path->freespace_units != NULL) + if (path->freespace_units != NULL) { free(path->freespace_units); + } set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); - if (path->freespace_percent != NULL) + if (path->freespace_percent != NULL) { free(path->freespace_percent); + } set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); - if (path->usedspace_units != NULL) + if (path->usedspace_units != NULL) { free(path->usedspace_units); + } set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); - if (path->usedspace_percent != NULL) + if (path->usedspace_percent != NULL) { free(path->usedspace_percent); + } set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); - if (path->usedinodes_percent != NULL) + if (path->usedinodes_percent != NULL) { free(path->usedinodes_percent); + } set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); - if (path->freeinodes_percent != NULL) + if (path->freeinodes_percent != NULL) { free(path->freeinodes_percent); + } set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); } @@ -1011,11 +1048,13 @@ void print_usage(void) { bool stat_path(struct parameter_list *p) { /* Stat entry to check that dir exists and is accessible */ - if (verbose >= 3) + if (verbose >= 3) { printf("calling stat on %s\n", p->name); + } if (stat(p->name, &stat_buf[0])) { - if (verbose >= 3) + if (verbose >= 3) { printf("stat failed on %s\n", p->name); + } if (ignore_missing == true) { return false; } @@ -1036,18 +1075,21 @@ void get_stats(struct parameter_list *p, struct fs_usage *fsp) { /* find all group members */ for (p_list = path_select_list; p_list; p_list = p_list->name_next) { #ifdef __CYGWIN__ - if (strncmp(p_list->name, "/cygdrive/", 10) != 0) + if (strncmp(p_list->name, "/cygdrive/", 10) != 0) { continue; + } #endif if (p_list->group && !(strcmp(p_list->group, p->group))) { - if (!stat_path(p_list)) + if (!stat_path(p_list)) { continue; + } get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp); get_path_stats(p_list, &tmpfsp); - if (verbose >= 3) + if (verbose >= 3) { printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n", p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, p_list->dfree_units, p_list->dtotal_units, mult); + } /* prevent counting the first FS of a group twice since its parameter_list entry * is used to carry the information of all file systems of the entire group */ @@ -1067,9 +1109,10 @@ void get_stats(struct parameter_list *p, struct fs_usage *fsp) { } first = 0; } - if (verbose >= 3) + if (verbose >= 3) { printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p->group, p->dused_units, p->dfree_units, p->dtotal_units, tmpfsp.fsu_blocksize, mult); + } } /* modify devname and mountdir for output */ p->best_match->me_mountdir = p->best_match->me_devname = p->group; -- cgit v1.2.3-74-g34f1 From 969f40c2a083b4e0654a46e6b9e37a0378a4f332 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:44:28 +0100 Subject: check_disk: boolean type and linter fixes --- plugins/check_disk.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index c3534060..b5a375d0 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -62,10 +62,10 @@ const char *email = "devel@monitoring-plugins.org"; static int show_all_fs = 1; /* If nonzero, show only local filesystems. */ -static int show_local_fs = 0; +static bool show_local_fs = false; /* If nonzero, show only local filesystems but call stat() on remote ones. */ -static int stat_remote_fs = 0; +static bool stat_remote_fs = false; /* If positive, the units to use when printing sizes; if negative, the human-readable base. */ @@ -121,7 +121,7 @@ static int process_arguments(int /*argc*/, char ** /*argv*/); static void set_all_thresholds(struct parameter_list *path); static void print_help(void); void print_usage(void); -static double calculate_percent(uintmax_t, uintmax_t); +static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/); static bool stat_path(struct parameter_list *p); static void get_stats(struct parameter_list *p, struct fs_usage *fsp); static void get_path_stats(struct parameter_list *p, struct fs_usage *fsp); @@ -198,7 +198,7 @@ int main(int argc, char **argv) { /* If a list of paths has not been selected, find entire mount list and create list of paths */ - if (path_selected == false && path_ignored == false) { + if (!path_selected && !path_ignored) { for (me = mount_list; me; me = me->me_next) { if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { path = np_add_parameter(&path_select_list, me->me_mountdir); @@ -209,7 +209,7 @@ int main(int argc, char **argv) { } } - if (path_ignored == false) { + if (!path_ignored) { np_set_best_match(path_select_list, mount_list, exact_match); } @@ -217,7 +217,7 @@ int main(int argc, char **argv) { temp_list = path_select_list; while (path_select_list) { - if (!path_select_list->best_match && ignore_missing == true) { + if (!path_select_list->best_match && ignore_missing) { /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ if (path_select_list == temp_list) { temp_list = path_select_list->name_next; @@ -237,7 +237,7 @@ int main(int argc, char **argv) { path_select_list = temp_list; - if (!path_select_list && ignore_missing == true) { + if (!path_select_list && ignore_missing) { result = STATE_OK; if (verbose >= 2) { printf("None of the provided paths were found\n"); @@ -285,7 +285,7 @@ int main(int argc, char **argv) { /* Skip remote filesystems if we're not interested in them */ if (me->me_remote && show_local_fs) { if (stat_remote_fs) { - if (!stat_path(path) && ignore_missing == true) { + if (!stat_path(path) && ignore_missing) { result = STATE_OK; xasprintf(&ignored, "%s %s;", ignored, path->name); } @@ -311,7 +311,7 @@ int main(int argc, char **argv) { } if (!stat_path(path)) { - if (ignore_missing == true) { + if (ignore_missing) { result = STATE_OK; xasprintf(&ignored, "%s %s;", ignored, path->name); } @@ -398,8 +398,8 @@ int main(int argc, char **argv) { /* Nb: *_high_tide are unset when == UINT64_MAX */ xasprintf(&perf, "%s %s", perf, perfdata_uint64((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, - path->dused_units * mult, "B", (warning_high_tide == UINT64_MAX ? false : true), warning_high_tide, - (critical_high_tide == UINT64_MAX ? false : true), critical_high_tide, true, 0, true, + path->dused_units * mult, "B", (warning_high_tide != UINT64_MAX ), warning_high_tide, + (critical_high_tide != UINT64_MAX ), critical_high_tide, true, 0, true, path->dtotal_units * mult)); if (display_inodes_perfdata) { @@ -420,8 +420,8 @@ int main(int argc, char **argv) { (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir); /* Nb: *_high_tide are unset when == UINT64_MAX */ xasprintf(&perf, "%s %s", perf, - perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX ? true : false), - warning_high_tide, (critical_high_tide != UINT64_MAX ? true : false), critical_high_tide, true, 0, + perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX), + warning_high_tide, (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, path->inodes_total)); } @@ -669,13 +669,13 @@ int process_arguments(int argc, char **argv) { units = strdup("MiB"); break; case 'L': - stat_remote_fs = 1; + stat_remote_fs = true; /* fallthrough */ case 'l': - show_local_fs = 1; + show_local_fs = true; break; case 'P': - display_inodes_perfdata = 1; + display_inodes_perfdata = true; break; case 'p': /* select path */ if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || @@ -688,7 +688,7 @@ int process_arguments(int argc, char **argv) { if (!(se = np_find_parameter(path_select_list, optarg))) { se = np_add_parameter(&path_select_list, optarg); - if (stat(optarg, &stat_buf[0]) && ignore_missing == true) { + if (stat(optarg, &stat_buf[0]) && ignore_missing) { path_ignored = true; break; } @@ -832,7 +832,7 @@ int process_arguments(int argc, char **argv) { } } - if (!fnd && ignore_missing == true) { + if (!fnd && ignore_missing) { path_ignored = true; path_selected = true; break; @@ -852,7 +852,7 @@ int process_arguments(int argc, char **argv) { break; case 'C': /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ - if (path_selected == false) { + if (!path_selected) { struct parameter_list *path; for (me = mount_list; me; me = me->me_next) { if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { @@ -1055,7 +1055,7 @@ bool stat_path(struct parameter_list *p) { if (verbose >= 3) { printf("stat failed on %s\n", p->name); } - if (ignore_missing == true) { + if (ignore_missing) { return false; } printf("DISK %s - ", _("CRITICAL")); -- cgit v1.2.3-74-g34f1 From 4fb7fb05b672febfdfa69381f7f403dc872c7aa6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 17 Mar 2025 19:36:11 +0100 Subject: check_disk: General refactoring --- plugins/check_disk.c | 353 +++++++++++++++++++++++---------------------------- 1 file changed, 158 insertions(+), 195 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index b5a375d0..e16c453d 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -31,13 +31,17 @@ const char *program_name = "check_disk"; /* Required for coreutils libs */ const char *copyright = "1999-2024"; const char *email = "devel@monitoring-plugins.org"; +#include "states.h" #include "common.h" + #ifdef HAVE_SYS_STAT_H # include #endif + #if HAVE_INTTYPES_H # include #endif + #include #include "popen.h" #include "utils.h" @@ -46,9 +50,11 @@ const char *email = "devel@monitoring-plugins.org"; #include "fsusage.h" #include "mountlist.h" #include + #if HAVE_LIMITS_H # include #endif + #include "regex.h" #ifdef __CYGWIN__ @@ -57,39 +63,12 @@ const char *email = "devel@monitoring-plugins.org"; # define ERROR -1 #endif -/* If nonzero, show even filesystems with zero size or - uninteresting types. */ -static int show_all_fs = 1; - /* If nonzero, show only local filesystems. */ static bool show_local_fs = false; /* If nonzero, show only local filesystems but call stat() on remote ones. */ static bool stat_remote_fs = false; -/* If positive, the units to use when printing sizes; - if negative, the human-readable base. */ -/* static int output_block_size; */ - -/* If nonzero, invoke the `sync' system call before getting any usage data. - Using this option can make df very slow, especially with many or very - busy disks. Note that this may make a difference on some systems -- - SunOs4.1.3, for one. It is *not* necessary on Linux. */ -/* static int require_sync = 0; */ - -/* Linked list of filesystem types to display. - If `fs_select_list' is NULL, list all types. - This table is generated dynamically from command-line options, - rather than hardcoding into the program what it thinks are the - valid filesystem types; let the user specify any filesystem type - they want to, and if there are any filesystems of that type, they - will be shown. - - Some filesystem types: - 4.2 4.3 ufs nfs swap ignore io vm efs dbg */ - -/* static struct parameter_list *fs_select_list; */ - /* Linked list of filesystem types to omit. If the list is empty, don't exclude any types. */ static struct regex_list *fs_exclude_list = NULL; @@ -150,43 +129,18 @@ static char *crit_freeinodes_percent = NULL; static bool path_selected = false; static bool path_ignored = false; static char *group = NULL; -static struct stat *stat_buf; static struct name_list *seen = NULL; int main(int argc, char **argv) { - int result = STATE_UNKNOWN; - int disk_result = STATE_UNKNOWN; - char *output = NULL; - char *ignored = NULL; - char *details = NULL; - char *perf = NULL; - char *perf_ilabel = NULL; - char *preamble = " - free space:"; - char *ignored_preamble = " - ignored paths:"; - char *flag_header = NULL; - int temp_result = STATE_UNKNOWN; - - struct mount_entry *me = NULL; - struct fs_usage fsp = {0}; - struct parameter_list *temp_list = NULL; - struct parameter_list *path = NULL; + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); #ifdef __CYGWIN__ char mountdir[32]; #endif - output = strdup(""); - ignored = strdup(""); - details = strdup(""); - perf = strdup(""); - perf_ilabel = strdup(""); - stat_buf = malloc(sizeof *stat_buf); - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - mount_list = read_file_system_list(0); + mount_list = read_file_system_list(false); /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); @@ -199,7 +153,8 @@ int main(int argc, char **argv) { mount list and create list of paths */ if (!path_selected && !path_ignored) { - for (me = mount_list; me; me = me->me_next) { + for (struct mount_entry *me = mount_list; me; me = me->me_next) { + struct parameter_list *path = NULL; if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { path = np_add_parameter(&path_select_list, me->me_mountdir); } @@ -214,8 +169,9 @@ int main(int argc, char **argv) { } /* Error if no match found for specified paths */ - temp_list = path_select_list; + struct parameter_list *temp_list = path_select_list; + char *ignored = strdup(""); while (path_select_list) { if (!path_select_list->best_match && ignore_missing) { /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ @@ -237,6 +193,7 @@ int main(int argc, char **argv) { path_select_list = temp_list; + int result = STATE_UNKNOWN; if (!path_select_list && ignore_missing) { result = STATE_OK; if (verbose >= 2) { @@ -244,6 +201,11 @@ int main(int argc, char **argv) { } } + mp_state_enum disk_result = STATE_UNKNOWN; + char *perf = strdup(""); + char *perf_ilabel = strdup(""); + char *output = strdup(""); + struct parameter_list *path = NULL; /* Process for every path in list */ for (path = path_select_list; path; path = path->name_next) { if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) { @@ -255,12 +217,9 @@ int main(int argc, char **argv) { printf("Group of %s: %s\n", path->name, path->group); } - /* reset disk result */ - disk_result = STATE_UNKNOWN; + struct mount_entry *mount_entry = path->best_match; - me = path->best_match; - - if (!me) { + if (!mount_entry) { continue; } @@ -276,14 +235,14 @@ int main(int argc, char **argv) { /* Filters */ /* Remove filesystems already seen */ - if (np_seen_name(seen, me->me_mountdir)) { + if (np_seen_name(seen, mount_entry->me_mountdir)) { continue; } - np_add_name(&seen, me->me_mountdir); + np_add_name(&seen, mount_entry->me_mountdir); if (path->group == NULL) { /* Skip remote filesystems if we're not interested in them */ - if (me->me_remote && show_local_fs) { + if (mount_entry->me_remote && show_local_fs) { if (stat_remote_fs) { if (!stat_path(path) && ignore_missing) { result = STATE_OK; @@ -293,19 +252,16 @@ int main(int argc, char **argv) { continue; /* Skip pseudo fs's if we haven't asked for all fs's */ } - if (me->me_dummy && !show_all_fs) { - continue; - /* Skip excluded fstypes */ - } - if (fs_exclude_list && np_find_regmatch(fs_exclude_list, me->me_type)) { + if (fs_exclude_list && np_find_regmatch(fs_exclude_list, mount_entry->me_type)) { continue; /* Skip excluded fs's */ } - if (dp_exclude_list && (np_find_name(dp_exclude_list, me->me_devname) || np_find_name(dp_exclude_list, me->me_mountdir))) { + if (dp_exclude_list && + (np_find_name(dp_exclude_list, mount_entry->me_devname) || np_find_name(dp_exclude_list, mount_entry->me_mountdir))) { continue; /* Skip not included fstypes */ } - if (fs_include_list && !np_find_regmatch(fs_include_list, me->me_type)) { + if (fs_include_list && !np_find_regmatch(fs_include_list, mount_entry->me_type)) { continue; } } @@ -317,21 +273,23 @@ int main(int argc, char **argv) { } continue; } - get_fs_usage(me->me_mountdir, me->me_devname, &fsp); - if (fsp.fsu_blocks && strcmp("none", me->me_mountdir)) { + struct fs_usage fsp = {0}; + get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp); + + if (fsp.fsu_blocks && strcmp("none", mount_entry->me_mountdir)) { get_stats(path, &fsp); if (verbose >= 3) { printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f " "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n", - me->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units, + mount_entry->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units, path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult); } /* Threshold comparisons */ - temp_result = get_status(path->dfree_units, path->freespace_units); + mp_state_enum temp_result = get_status(path->dfree_units, path->freespace_units); if (verbose >= 3) { printf("Freespace_units result=%d\n", temp_result); } @@ -397,10 +355,10 @@ int main(int argc, char **argv) { /* Nb: *_high_tide are unset when == UINT64_MAX */ xasprintf(&perf, "%s %s", perf, - perfdata_uint64((!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, - path->dused_units * mult, "B", (warning_high_tide != UINT64_MAX ), warning_high_tide, - (critical_high_tide != UINT64_MAX ), critical_high_tide, true, 0, true, - path->dtotal_units * mult)); + perfdata_uint64((!strcmp(mount_entry->me_mountdir, "none") || display_mntp) ? mount_entry->me_devname + : mount_entry->me_mountdir, + path->dused_units * mult, "B", (warning_high_tide != UINT64_MAX), warning_high_tide, + (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, path->dtotal_units * mult)); if (display_inodes_perfdata) { /* *_high_tide must be reinitialized at each run */ @@ -417,26 +375,26 @@ int main(int argc, char **argv) { } xasprintf(&perf_ilabel, "%s (inodes)", - (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir); + (!strcmp(mount_entry->me_mountdir, "none") || display_mntp) ? mount_entry->me_devname : mount_entry->me_mountdir); /* Nb: *_high_tide are unset when == UINT64_MAX */ xasprintf(&perf, "%s %s", perf, - perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX), - warning_high_tide, (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, - true, path->inodes_total)); + perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX), warning_high_tide, + (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, path->inodes_total)); } if (disk_result == STATE_OK && erronly && !verbose) { continue; } + char *flag_header = NULL; if (disk_result && verbose >= 1) { xasprintf(&flag_header, " %s [", state_text(disk_result)); } else { xasprintf(&flag_header, ""); } xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, - (!strcmp(me->me_mountdir, "none") || display_mntp) ? me->me_devname : me->me_mountdir, path->dfree_units, units, - path->dfree_pct); + (!strcmp(mount_entry->me_mountdir, "none") || display_mntp) ? mount_entry->me_devname : mount_entry->me_mountdir, + path->dfree_units, units, path->dfree_pct); if (path->dused_inodes_percent < 0) { xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : "")); } else { @@ -446,15 +404,13 @@ int main(int argc, char **argv) { } } - if (verbose >= 2) { - xasprintf(&output, "%s%s", output, details); - } - + char *preamble = " - free space:"; if (strcmp(output, "") == 0 && !erronly) { preamble = ""; xasprintf(&output, " - No disks were found for provided parameters"); } + char *ignored_preamble = " - ignored paths:"; printf("DISK %s%s%s%s%s|%s\n", state_text(result), ((erronly && result == STATE_OK)) ? "" : preamble, output, (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf); return result; @@ -470,19 +426,10 @@ double calculate_percent(uintmax_t value, uintmax_t total) { /* process command-line arguments */ int process_arguments(int argc, char **argv) { - int c; - int err; - struct parameter_list *se; - struct parameter_list *temp_list = NULL; - struct parameter_list *previous = NULL; - struct mount_entry *me; - regex_t re; - int cflags = REG_NOSUB | REG_EXTENDED; - int default_cflags = cflags; - char errbuf[MAX_INPUT_BUFFER]; - int fnd = 0; + if (argc < 2) { + return ERROR; + } - int option = 0; static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, @@ -522,26 +469,26 @@ int process_arguments(int argc, char **argv) { {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; - if (argc < 2) { - return ERROR; + for (int index = 1; index < argc; index++) { + if (strcmp("-to", argv[index]) == 0) { + strcpy(argv[index], "-t"); + } } - np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); + int cflags = REG_NOSUB | REG_EXTENDED; + int default_cflags = cflags; - for (c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) { - strcpy(argv[c], "-t"); - } - } + np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); - while (1) { - c = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); + while (true) { + int option = 0; + int option_index = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); - if (c == -1 || c == EOF) { + if (option_index == -1 || option_index == EOF) { break; } - switch (c) { + switch (option_index) { case 't': /* timeout period */ if (is_integer(optarg)) { timeout_interval = atoi(optarg); @@ -685,10 +632,12 @@ int process_arguments(int argc, char **argv) { } /* add parameter if not found. overwrite thresholds if path has already been added */ + struct parameter_list *se; if (!(se = np_find_parameter(path_select_list, optarg))) { se = np_add_parameter(&path_select_list, optarg); - if (stat(optarg, &stat_buf[0]) && ignore_missing) { + struct stat stat_buf = {}; + if (stat(optarg, &stat_buf) && ignore_missing) { path_ignored = true; break; } @@ -703,7 +652,7 @@ int process_arguments(int argc, char **argv) { /* NB: We can't free the old mount_list "just like that": both list pointers and struct * pointers are copied around. One of the reason it wasn't done yet is that other parts * of check_disk need the same kind of cleanup so it'd better be done as a whole */ - mount_list = read_file_system_list(0); + mount_list = read_file_system_list(false); np_set_best_match(se, mount_list, exact_match); path_selected = true; @@ -711,9 +660,10 @@ int process_arguments(int argc, char **argv) { case 'x': /* exclude path or partition */ np_add_name(&dp_exclude_list, optarg); break; - case 'X': /* exclude file system type */ - err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); + case 'X': /* exclude file system type */ { + int err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); if (err != 0) { + char errbuf[MAX_INPUT_BUFFER]; regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } @@ -721,10 +671,11 @@ int process_arguments(int argc, char **argv) { case 'N': /* include file system type */ err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED); if (err != 0) { + char errbuf[MAX_INPUT_BUFFER]; regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } - break; + } break; case 'v': /* verbose */ verbose++; break; @@ -753,23 +704,24 @@ int process_arguments(int argc, char **argv) { case 'I': cflags |= REG_ICASE; // Intentional fallthrough - case 'i': + case 'i': { if (!path_selected) { die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); } - err = regcomp(&re, optarg, cflags); + regex_t regex; + int err = regcomp(®ex, optarg, cflags); if (err != 0) { - regerror(err, &re, errbuf, MAX_INPUT_BUFFER); + char errbuf[MAX_INPUT_BUFFER]; + regerror(err, ®ex, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } - temp_list = path_select_list; - - previous = NULL; + struct parameter_list *temp_list = path_select_list; + struct parameter_list *previous = NULL; while (temp_list) { if (temp_list->best_match) { - if (np_regex_match_mount_entry(temp_list->best_match, &re)) { + if (np_regex_match_mount_entry(temp_list->best_match, ®ex)) { if (verbose >= 3) { printf("ignoring %s matching regex\n", temp_list->name); @@ -791,8 +743,7 @@ int process_arguments(int argc, char **argv) { } cflags = default_cflags; - break; - + } break; case 'n': ignore_missing = true; break; @@ -802,7 +753,7 @@ int process_arguments(int argc, char **argv) { case 'R': cflags |= REG_ICASE; // Intentional fallthrough - case 'r': + case 'r': { if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { @@ -810,15 +761,18 @@ int process_arguments(int argc, char **argv) { _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); } - err = regcomp(&re, optarg, cflags); + regex_t regex; + int err = regcomp(®ex, optarg, cflags); if (err != 0) { - regerror(err, &re, errbuf, MAX_INPUT_BUFFER); + char errbuf[MAX_INPUT_BUFFER]; + regerror(err, ®ex, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } - for (me = mount_list; me; me = me->me_next) { - if (np_regex_match_mount_entry(me, &re)) { - fnd = true; + bool found = false; + for (struct mount_entry *me = mount_list; me; me = me->me_next) { + if (np_regex_match_mount_entry(me, ®ex)) { + found = true; if (verbose >= 3) { printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); } @@ -832,21 +786,21 @@ int process_arguments(int argc, char **argv) { } } - if (!fnd && ignore_missing) { + if (!found && ignore_missing) { path_ignored = true; path_selected = true; break; } - if (!fnd) { + if (!found) { die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); } - fnd = false; + found = false; path_selected = true; np_set_best_match(path_select_list, mount_list, exact_match); cflags = default_cflags; - break; + } break; case 'M': /* display mountpoint */ display_mntp = true; break; @@ -854,7 +808,7 @@ int process_arguments(int argc, char **argv) { /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ if (!path_selected) { struct parameter_list *path; - for (me = mount_list; me; me = me->me_next) { + for (struct mount_entry *me = mount_list; me; me = me->me_next) { if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { path = np_add_parameter(&path_select_list, me->me_mountdir); } @@ -891,17 +845,17 @@ int process_arguments(int argc, char **argv) { } /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ - c = optind; - if (warn_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) { - warn_usedspace_percent = argv[c++]; + int index = optind; + if (warn_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + warn_usedspace_percent = argv[index++]; } - if (crit_usedspace_percent == NULL && argc > c && is_intnonneg(argv[c])) { - crit_usedspace_percent = argv[c++]; + if (crit_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + crit_usedspace_percent = argv[index++]; } - if (argc > c) { - se = np_add_parameter(&path_select_list, strdup(argv[c++])); + if (argc > index) { + struct parameter_list *se = np_add_parameter(&path_select_list, strdup(argv[index++])); path_selected = true; set_all_thresholds(se); } @@ -911,7 +865,7 @@ int process_arguments(int argc, char **argv) { mult = (uintmax_t)1024 * 1024; } - return true; + return 0; } void set_all_thresholds(struct parameter_list *path) { @@ -919,22 +873,27 @@ void set_all_thresholds(struct parameter_list *path) { free(path->freespace_units); } set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); + if (path->freespace_percent != NULL) { free(path->freespace_percent); } set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); + if (path->usedspace_units != NULL) { free(path->usedspace_units); } set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); + if (path->usedspace_percent != NULL) { free(path->usedspace_percent); } set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); + if (path->usedinodes_percent != NULL) { free(path->usedinodes_percent); } set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); + if (path->freeinodes_percent != NULL) { free(path->freeinodes_percent); } @@ -1046,40 +1005,44 @@ void print_usage(void) { printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); } -bool stat_path(struct parameter_list *p) { +bool stat_path(struct parameter_list *parameters) { /* Stat entry to check that dir exists and is accessible */ if (verbose >= 3) { - printf("calling stat on %s\n", p->name); + printf("calling stat on %s\n", parameters->name); } - if (stat(p->name, &stat_buf[0])) { + + struct stat stat_buf = {0}; + if (stat(parameters->name, &stat_buf)) { if (verbose >= 3) { - printf("stat failed on %s\n", p->name); + printf("stat failed on %s\n", parameters->name); } if (ignore_missing) { return false; } printf("DISK %s - ", _("CRITICAL")); - die(STATE_CRITICAL, _("%s %s: %s\n"), p->name, _("is not accessible"), strerror(errno)); + die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"), strerror(errno)); } + return true; } -void get_stats(struct parameter_list *p, struct fs_usage *fsp) { - struct parameter_list *p_list; +void get_stats(struct parameter_list *parameters, struct fs_usage *fsp) { struct fs_usage tmpfsp; - int first = 1; + bool first = true; - if (p->group == NULL) { - get_path_stats(p, fsp); + if (parameters->group == NULL) { + get_path_stats(parameters, fsp); } else { /* find all group members */ - for (p_list = path_select_list; p_list; p_list = p_list->name_next) { + for (struct parameter_list *p_list = path_select_list; p_list; p_list = p_list->name_next) { + #ifdef __CYGWIN__ if (strncmp(p_list->name, "/cygdrive/", 10) != 0) { continue; } #endif - if (p_list->group && !(strcmp(p_list->group, p->group))) { + + if (p_list->group && !(strcmp(p_list->group, parameters->group))) { if (!stat_path(p_list)) { continue; } @@ -1094,63 +1057,63 @@ void get_stats(struct parameter_list *p, struct fs_usage *fsp) { /* prevent counting the first FS of a group twice since its parameter_list entry * is used to carry the information of all file systems of the entire group */ if (!first) { - p->total += p_list->total; - p->available += p_list->available; - p->available_to_root += p_list->available_to_root; - p->used += p_list->used; - - p->dused_units += p_list->dused_units; - p->dfree_units += p_list->dfree_units; - p->dtotal_units += p_list->dtotal_units; - p->inodes_total += p_list->inodes_total; - p->inodes_free += p_list->inodes_free; - p->inodes_free_to_root += p_list->inodes_free_to_root; - p->inodes_used += p_list->inodes_used; + parameters->total += p_list->total; + parameters->available += p_list->available; + parameters->available_to_root += p_list->available_to_root; + parameters->used += p_list->used; + + parameters->dused_units += p_list->dused_units; + parameters->dfree_units += p_list->dfree_units; + parameters->dtotal_units += p_list->dtotal_units; + parameters->inodes_total += p_list->inodes_total; + parameters->inodes_free += p_list->inodes_free; + parameters->inodes_free_to_root += p_list->inodes_free_to_root; + parameters->inodes_used += p_list->inodes_used; } - first = 0; + first = false; } if (verbose >= 3) { - printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", p->group, - p->dused_units, p->dfree_units, p->dtotal_units, tmpfsp.fsu_blocksize, mult); + printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", parameters->group, + parameters->dused_units, parameters->dfree_units, parameters->dtotal_units, tmpfsp.fsu_blocksize, mult); } } /* modify devname and mountdir for output */ - p->best_match->me_mountdir = p->best_match->me_devname = p->group; + parameters->best_match->me_mountdir = parameters->best_match->me_devname = parameters->group; } /* finally calculate percentages for either plain FS or summed up group */ - p->dused_pct = calculate_percent(p->used, p->used + p->available); /* used + available can never be > uintmax */ - p->dfree_pct = 100.0 - p->dused_pct; - p->dused_inodes_percent = calculate_percent(p->inodes_total - p->inodes_free, p->inodes_total); - p->dfree_inodes_percent = 100 - p->dused_inodes_percent; + parameters->dused_pct = calculate_percent(parameters->used, parameters->used + parameters->available); /* used + available can never be > uintmax */ + parameters->dfree_pct = 100.0 - parameters->dused_pct; + parameters->dused_inodes_percent = calculate_percent(parameters->inodes_total - parameters->inodes_free, parameters->inodes_total); + parameters->dfree_inodes_percent = 100 - parameters->dused_inodes_percent; } -void get_path_stats(struct parameter_list *p, struct fs_usage *fsp) { - p->available = fsp->fsu_bavail; - p->available_to_root = fsp->fsu_bfree; - p->used = fsp->fsu_blocks - fsp->fsu_bfree; +void get_path_stats(struct parameter_list *parameters, struct fs_usage *fsp) { + parameters->available = fsp->fsu_bavail; + parameters->available_to_root = fsp->fsu_bfree; + parameters->used = fsp->fsu_blocks - fsp->fsu_bfree; if (freespace_ignore_reserved) { /* option activated : we subtract the root-reserved space from the total */ - p->total = fsp->fsu_blocks - p->available_to_root + p->available; + parameters->total = fsp->fsu_blocks - parameters->available_to_root + parameters->available; } else { /* default behaviour : take all the blocks into account */ - p->total = fsp->fsu_blocks; + parameters->total = fsp->fsu_blocks; } - p->dused_units = p->used * fsp->fsu_blocksize / mult; - p->dfree_units = p->available * fsp->fsu_blocksize / mult; - p->dtotal_units = p->total * fsp->fsu_blocksize / mult; + parameters->dused_units = parameters->used * fsp->fsu_blocksize / mult; + parameters->dfree_units = parameters->available * fsp->fsu_blocksize / mult; + parameters->dtotal_units = parameters->total * fsp->fsu_blocksize / mult; /* Free file nodes. Not sure the workaround is required, but in case...*/ - p->inodes_free = fsp->fsu_ffree; - p->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ - p->inodes_used = fsp->fsu_files - fsp->fsu_ffree; + parameters->inodes_free = fsp->fsu_ffree; + parameters->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ + parameters->inodes_used = fsp->fsu_files - fsp->fsu_ffree; if (freespace_ignore_reserved) { /* option activated : we subtract the root-reserved inodes from the total */ /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ /* for others, fsp->fsu_ffree == fsp->fsu_favail */ - p->inodes_total = fsp->fsu_files - p->inodes_free_to_root + p->inodes_free; + parameters->inodes_total = fsp->fsu_files - parameters->inodes_free_to_root + parameters->inodes_free; } else { /* default behaviour : take all the inodes into account */ - p->inodes_total = fsp->fsu_files; + parameters->inodes_total = fsp->fsu_files; } - np_add_name(&seen, p->best_match->me_mountdir); + np_add_name(&seen, parameters->best_match->me_mountdir); } -- cgit v1.2.3-74-g34f1 From ef3045b97eea23a4dec3197277e5ff4e5afc5c71 Mon Sep 17 00:00:00 2001 From: Andre Klärner Date: Mon, 17 Mar 2025 16:13:46 +0100 Subject: change error message for missing certificate The old error message is quite similar to the openssl `failed to retrieve issuer certificate` and can mislead users to troubleshooting certificate stores. The new message should be distinct enough to make it clear to users that this is not a problem raised by the underlying SSL implementation, but a problem inside monitoring-plugins. --- plugins/sslutils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sslutils.c b/plugins/sslutils.c index 719de575..96740b3a 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -201,7 +201,7 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int time_t tm_t; if (!certificate) { - printf("%s\n", _("CRITICAL - Cannot retrieve server certificate.")); + printf("%s\n", _("CRITICAL - No server certificate present to inspect.")); return STATE_CRITICAL; } -- cgit v1.2.3-74-g34f1 From 7b53cbbd265ed135941bf59da77ed22b8664b6eb Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 17 Mar 2025 20:21:58 +0100 Subject: check_disk: Little fixes and improvements --- plugins/check_disk.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index e16c453d..a333a8b5 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -101,9 +101,9 @@ static void set_all_thresholds(struct parameter_list *path); static void print_help(void); void print_usage(void); static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/); -static bool stat_path(struct parameter_list *p); -static void get_stats(struct parameter_list *p, struct fs_usage *fsp); -static void get_path_stats(struct parameter_list *p, struct fs_usage *fsp); +static bool stat_path(struct parameter_list * /*parameters*/); +static void get_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp); +static void get_path_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp); static char *units; static uintmax_t mult = 1024 * 1024; @@ -624,7 +624,7 @@ int process_arguments(int argc, char **argv) { case 'P': display_inodes_perfdata = true; break; - case 'p': /* select path */ + case 'p': /* select path */ { if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { @@ -656,7 +656,7 @@ int process_arguments(int argc, char **argv) { np_set_best_match(se, mount_list, exact_match); path_selected = true; - break; + } break; case 'x': /* exclude path or partition */ np_add_name(&dp_exclude_list, optarg); break; @@ -778,6 +778,7 @@ int process_arguments(int argc, char **argv) { } /* add parameter if not found. overwrite thresholds if path has already been added */ + struct parameter_list *se = NULL; if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { se = np_add_parameter(&path_select_list, me->me_mountdir); } @@ -804,7 +805,7 @@ int process_arguments(int argc, char **argv) { case 'M': /* display mountpoint */ display_mntp = true; break; - case 'C': + case 'C': { /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ if (!path_selected) { struct parameter_list *path; @@ -832,7 +833,7 @@ int process_arguments(int argc, char **argv) { path_selected = false; group = NULL; - break; + } break; case 'V': /* version */ print_revision(progname, NP_VERSION); exit(STATE_UNKNOWN); @@ -843,18 +844,31 @@ int process_arguments(int argc, char **argv) { usage(_("Unknown argument")); } } + if (verbose > 0) { + printf("ping\n"); + } /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ int index = optind; + if (warn_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (verbose > 0) { + printf("Got an positional warn threshold: %s\n", argv[index]); + } warn_usedspace_percent = argv[index++]; } if (crit_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (verbose > 0) { + printf("Got an positional crit threshold: %s\n", argv[index]); + } crit_usedspace_percent = argv[index++]; } if (argc > index) { + if (verbose > 0) { + printf("Got an positional filesystem: %s\n", argv[index]); + } struct parameter_list *se = np_add_parameter(&path_select_list, strdup(argv[index++])); path_selected = true; set_all_thresholds(se); @@ -1081,7 +1095,8 @@ void get_stats(struct parameter_list *parameters, struct fs_usage *fsp) { parameters->best_match->me_mountdir = parameters->best_match->me_devname = parameters->group; } /* finally calculate percentages for either plain FS or summed up group */ - parameters->dused_pct = calculate_percent(parameters->used, parameters->used + parameters->available); /* used + available can never be > uintmax */ + parameters->dused_pct = + calculate_percent(parameters->used, parameters->used + parameters->available); /* used + available can never be > uintmax */ parameters->dfree_pct = 100.0 - parameters->dused_pct; parameters->dused_inodes_percent = calculate_percent(parameters->inodes_total - parameters->inodes_free, parameters->inodes_total); parameters->dfree_inodes_percent = 100 - parameters->dused_inodes_percent; -- cgit v1.2.3-74-g34f1 From 096afc90a79f462e6c705764451273a887fd8c0e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 17 Mar 2025 20:37:28 +0100 Subject: check_disk: reset single file system result in between checks --- plugins/check_disk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index a333a8b5..f67f3d57 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -217,6 +217,9 @@ int main(int argc, char **argv) { printf("Group of %s: %s\n", path->name, path->group); } + // reset disk result + disk_result = STATE_UNKNOWN; + struct mount_entry *mount_entry = path->best_match; if (!mount_entry) { -- cgit v1.2.3-74-g34f1 From 285db2a9fa25519cacd48a76347ae2dee0c06605 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 14:36:20 +0100 Subject: Move disk specific stuff from lib to plugin specific directory --- lib/Makefile.am | 3 +- lib/utils_disk.c | 255 -------------------------------------- lib/utils_disk.h | 48 ------- plugins/Makefile.am | 2 + plugins/check_disk.d/utils_disk.c | 255 ++++++++++++++++++++++++++++++++++++++ plugins/check_disk.d/utils_disk.h | 48 +++++++ 6 files changed, 306 insertions(+), 305 deletions(-) delete mode 100644 lib/utils_disk.c delete mode 100644 lib/utils_disk.h create mode 100644 plugins/check_disk.d/utils_disk.c create mode 100644 plugins/check_disk.d/utils_disk.h diff --git a/lib/Makefile.am b/lib/Makefile.am index e41201c4..a9f3ff40 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -7,10 +7,9 @@ noinst_LIBRARIES = libmonitoringplug.a AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins -libmonitoringplug_a_SOURCES = utils_base.c utils_disk.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c +libmonitoringplug_a_SOURCES = utils_base.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c EXTRA_DIST = utils_base.h \ - utils_disk.h \ utils_tcp.h \ utils_cmd.h \ parse_ini.h \ diff --git a/lib/utils_disk.c b/lib/utils_disk.c deleted file mode 100644 index 2b761f5e..00000000 --- a/lib/utils_disk.c +++ /dev/null @@ -1,255 +0,0 @@ -/***************************************************************************** - * - * Library for check_disk - * - * License: GPL - * Copyright (c) 1999-2024 Monitoring Plugins Development Team - * - * Description: - * - * This file contains utilities for check_disk. These are tested by libtap - * - * - * 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" -#include "utils_disk.h" -#include "gl/fsusage.h" -#include - -void np_add_name(struct name_list **list, const char *name) { - struct name_list *new_entry; - new_entry = (struct name_list *)malloc(sizeof *new_entry); - new_entry->name = (char *)name; - new_entry->next = *list; - *list = new_entry; -} - -/* @brief Initialises a new regex at the begin of list via regcomp(3) - * - * @details if the regex fails to compile the error code of regcomp(3) is returned - * and list is not modified, otherwise list is modified to point to the new - * element - * @param list Pointer to a linked list of regex_list elements - * @param regex the string containing the regex which should be inserted into the list - * @param clags the cflags parameter for regcomp(3) - */ -int np_add_regex(struct regex_list **list, const char *regex, int cflags) { - struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry); - - if (new_entry == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - int regcomp_result = regcomp(&new_entry->regex, regex, cflags); - - if (!regcomp_result) { - // regcomp succeeded - new_entry->next = *list; - *list = new_entry; - - return 0; - } else { - // regcomp failed - free(new_entry); - - return regcomp_result; - } -} - -/* Initialises a new parameter at the end of list */ -struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name) { - struct parameter_list *current = *list; - struct parameter_list *new_path; - new_path = (struct parameter_list *)malloc(sizeof *new_path); - new_path->name = (char *)malloc(strlen(name) + 1); - new_path->best_match = NULL; - new_path->name_next = NULL; - new_path->name_prev = NULL; - new_path->freespace_bytes = NULL; - new_path->freespace_units = NULL; - new_path->freespace_percent = NULL; - new_path->usedspace_bytes = NULL; - new_path->usedspace_units = NULL; - new_path->usedspace_percent = NULL; - new_path->usedinodes_percent = NULL; - new_path->freeinodes_percent = NULL; - new_path->group = NULL; - new_path->dfree_pct = -1; - new_path->dused_pct = -1; - new_path->total = 0; - new_path->available = 0; - new_path->available_to_root = 0; - new_path->used = 0; - new_path->dused_units = 0; - new_path->dfree_units = 0; - new_path->dtotal_units = 0; - new_path->inodes_total = 0; - new_path->inodes_free = 0; - new_path->inodes_free_to_root = 0; - new_path->inodes_used = 0; - new_path->dused_inodes_percent = 0; - new_path->dfree_inodes_percent = 0; - - strcpy(new_path->name, name); - - if (current == NULL) { - *list = new_path; - new_path->name_prev = NULL; - } else { - while (current->name_next) { - current = current->name_next; - } - current->name_next = new_path; - new_path->name_prev = current; - } - return new_path; -} - -/* Delete a given parameter from list and return pointer to next element*/ -struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev) { - if (item == NULL) { - return NULL; - } - struct parameter_list *next; - - if (item->name_next) - next = item->name_next; - else - next = NULL; - - if (next) - next->name_prev = prev; - - if (prev) - prev->name_next = next; - - if (item->name) { - free(item->name); - } - free(item); - - return next; -} - -/* returns a pointer to the struct found in the list */ -struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name) { - struct parameter_list *temp_list; - for (temp_list = list; temp_list; temp_list = temp_list->name_next) { - if (!strcmp(temp_list->name, name)) - return temp_list; - } - - return NULL; -} - -void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact) { - struct parameter_list *d; - for (d = desired; d; d = d->name_next) { - if (!d->best_match) { - struct mount_entry *me; - size_t name_len = strlen(d->name); - size_t best_match_len = 0; - struct mount_entry *best_match = NULL; - struct fs_usage fsp; - - /* set best match if path name exactly matches a mounted device name */ - for (me = mount_list; me; me = me->me_next) { - if (strcmp(me->me_devname, d->name) == 0) { - if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { - best_match = me; - } - } - } - - /* set best match by directory name if no match was found by devname */ - if (!best_match) { - for (me = mount_list; me; me = me->me_next) { - size_t len = strlen(me->me_mountdir); - if ((!exact && - (best_match_len <= len && len <= name_len && (len == 1 || strncmp(me->me_mountdir, d->name, len) == 0))) || - (exact && strcmp(me->me_mountdir, d->name) == 0)) { - if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { - best_match = me; - best_match_len = len; - } - } - } - } - - if (best_match) { - d->best_match = best_match; - } else { - d->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */ - } - } - } -} - -/* Returns true if name is in list */ -bool np_find_name(struct name_list *list, const char *name) { - const struct name_list *n; - - if (list == NULL || name == NULL) { - return false; - } - for (n = list; n; n = n->next) { - if (!strcmp(name, n->name)) { - return true; - } - } - return false; -} - -/* Returns true if name is in list */ -bool np_find_regmatch(struct regex_list *list, const char *name) { - int len; - regmatch_t m; - - if (name == NULL) { - return false; - } - - len = strlen(name); - - for (; list; list = list->next) { - /* Emulate a full match as if surrounded with ^( )$ - by checking whether the match spans the whole name */ - if (!regexec(&list->regex, name, 1, &m, 0) && m.rm_so == 0 && m.rm_eo == len) { - return true; - } - } - - return false; -} - -bool np_seen_name(struct name_list *list, const char *name) { - const struct name_list *s; - for (s = list; s; s = s->next) { - if (!strcmp(s->name, name)) { - return true; - } - } - return false; -} - -bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { - if (regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0 || regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0) { - return true; - } - return false; -} diff --git a/lib/utils_disk.h b/lib/utils_disk.h deleted file mode 100644 index c5e81dc1..00000000 --- a/lib/utils_disk.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Header file for utils_disk */ - -#include "mountlist.h" -#include "utils_base.h" -#include "regex.h" - -struct name_list { - char *name; - struct name_list *next; -}; - -struct regex_list { - regex_t regex; - struct regex_list *next; -}; - -struct parameter_list { - char *name; - thresholds *freespace_bytes; - thresholds *freespace_units; - thresholds *freespace_percent; - thresholds *usedspace_bytes; - thresholds *usedspace_units; - thresholds *usedspace_percent; - thresholds *usedinodes_percent; - thresholds *freeinodes_percent; - char *group; - struct mount_entry *best_match; - struct parameter_list *name_next; - struct parameter_list *name_prev; - uintmax_t total, available, available_to_root, used, inodes_free, inodes_free_to_root, inodes_used, inodes_total; - double dfree_pct, dused_pct; - uint64_t dused_units, dfree_units, dtotal_units; - double dused_inodes_percent, dfree_inodes_percent; -}; - -void np_add_name(struct name_list **list, const char *name); -bool np_find_name(struct name_list *list, const char *name); -bool np_seen_name(struct name_list *list, const char *name); -int np_add_regex(struct regex_list **list, const char *regex, int cflags); -bool np_find_regmatch(struct regex_list *list, const char *name); -struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name); -struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name); -struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev); - -int search_parameter_list(struct parameter_list *list, const char *name); -void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact); -bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re); diff --git a/plugins/Makefile.am b/plugins/Makefile.am index e2bed4c3..30283cb4 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -55,6 +55,7 @@ EXTRA_DIST = t \ check_hpjd.d \ check_game.d \ check_radius.d \ + check_disk.d \ check_time.d \ check_nagios.d \ check_dbi.d \ @@ -119,6 +120,7 @@ check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohtt check_dbi_LDADD = $(NETLIBS) $(DBILIBS) check_dig_LDADD = $(NETLIBS) check_disk_LDADD = $(BASEOBJS) +check_disk_SOURCES = check_disk.c check_disk.d/utils_disk.c check_dns_LDADD = $(NETLIBS) check_dummy_LDADD = $(BASEOBJS) check_fping_LDADD = $(NETLIBS) diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c new file mode 100644 index 00000000..2b761f5e --- /dev/null +++ b/plugins/check_disk.d/utils_disk.c @@ -0,0 +1,255 @@ +/***************************************************************************** + * + * Library for check_disk + * + * License: GPL + * Copyright (c) 1999-2024 Monitoring Plugins Development Team + * + * Description: + * + * This file contains utilities for check_disk. These are tested by libtap + * + * + * 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" +#include "utils_disk.h" +#include "gl/fsusage.h" +#include + +void np_add_name(struct name_list **list, const char *name) { + struct name_list *new_entry; + new_entry = (struct name_list *)malloc(sizeof *new_entry); + new_entry->name = (char *)name; + new_entry->next = *list; + *list = new_entry; +} + +/* @brief Initialises a new regex at the begin of list via regcomp(3) + * + * @details if the regex fails to compile the error code of regcomp(3) is returned + * and list is not modified, otherwise list is modified to point to the new + * element + * @param list Pointer to a linked list of regex_list elements + * @param regex the string containing the regex which should be inserted into the list + * @param clags the cflags parameter for regcomp(3) + */ +int np_add_regex(struct regex_list **list, const char *regex, int cflags) { + struct regex_list *new_entry = (struct regex_list *)malloc(sizeof *new_entry); + + if (new_entry == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + int regcomp_result = regcomp(&new_entry->regex, regex, cflags); + + if (!regcomp_result) { + // regcomp succeeded + new_entry->next = *list; + *list = new_entry; + + return 0; + } else { + // regcomp failed + free(new_entry); + + return regcomp_result; + } +} + +/* Initialises a new parameter at the end of list */ +struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name) { + struct parameter_list *current = *list; + struct parameter_list *new_path; + new_path = (struct parameter_list *)malloc(sizeof *new_path); + new_path->name = (char *)malloc(strlen(name) + 1); + new_path->best_match = NULL; + new_path->name_next = NULL; + new_path->name_prev = NULL; + new_path->freespace_bytes = NULL; + new_path->freespace_units = NULL; + new_path->freespace_percent = NULL; + new_path->usedspace_bytes = NULL; + new_path->usedspace_units = NULL; + new_path->usedspace_percent = NULL; + new_path->usedinodes_percent = NULL; + new_path->freeinodes_percent = NULL; + new_path->group = NULL; + new_path->dfree_pct = -1; + new_path->dused_pct = -1; + new_path->total = 0; + new_path->available = 0; + new_path->available_to_root = 0; + new_path->used = 0; + new_path->dused_units = 0; + new_path->dfree_units = 0; + new_path->dtotal_units = 0; + new_path->inodes_total = 0; + new_path->inodes_free = 0; + new_path->inodes_free_to_root = 0; + new_path->inodes_used = 0; + new_path->dused_inodes_percent = 0; + new_path->dfree_inodes_percent = 0; + + strcpy(new_path->name, name); + + if (current == NULL) { + *list = new_path; + new_path->name_prev = NULL; + } else { + while (current->name_next) { + current = current->name_next; + } + current->name_next = new_path; + new_path->name_prev = current; + } + return new_path; +} + +/* Delete a given parameter from list and return pointer to next element*/ +struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev) { + if (item == NULL) { + return NULL; + } + struct parameter_list *next; + + if (item->name_next) + next = item->name_next; + else + next = NULL; + + if (next) + next->name_prev = prev; + + if (prev) + prev->name_next = next; + + if (item->name) { + free(item->name); + } + free(item); + + return next; +} + +/* returns a pointer to the struct found in the list */ +struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name) { + struct parameter_list *temp_list; + for (temp_list = list; temp_list; temp_list = temp_list->name_next) { + if (!strcmp(temp_list->name, name)) + return temp_list; + } + + return NULL; +} + +void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact) { + struct parameter_list *d; + for (d = desired; d; d = d->name_next) { + if (!d->best_match) { + struct mount_entry *me; + size_t name_len = strlen(d->name); + size_t best_match_len = 0; + struct mount_entry *best_match = NULL; + struct fs_usage fsp; + + /* set best match if path name exactly matches a mounted device name */ + for (me = mount_list; me; me = me->me_next) { + if (strcmp(me->me_devname, d->name) == 0) { + if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { + best_match = me; + } + } + } + + /* set best match by directory name if no match was found by devname */ + if (!best_match) { + for (me = mount_list; me; me = me->me_next) { + size_t len = strlen(me->me_mountdir); + if ((!exact && + (best_match_len <= len && len <= name_len && (len == 1 || strncmp(me->me_mountdir, d->name, len) == 0))) || + (exact && strcmp(me->me_mountdir, d->name) == 0)) { + if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { + best_match = me; + best_match_len = len; + } + } + } + } + + if (best_match) { + d->best_match = best_match; + } else { + d->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */ + } + } + } +} + +/* Returns true if name is in list */ +bool np_find_name(struct name_list *list, const char *name) { + const struct name_list *n; + + if (list == NULL || name == NULL) { + return false; + } + for (n = list; n; n = n->next) { + if (!strcmp(name, n->name)) { + return true; + } + } + return false; +} + +/* Returns true if name is in list */ +bool np_find_regmatch(struct regex_list *list, const char *name) { + int len; + regmatch_t m; + + if (name == NULL) { + return false; + } + + len = strlen(name); + + for (; list; list = list->next) { + /* Emulate a full match as if surrounded with ^( )$ + by checking whether the match spans the whole name */ + if (!regexec(&list->regex, name, 1, &m, 0) && m.rm_so == 0 && m.rm_eo == len) { + return true; + } + } + + return false; +} + +bool np_seen_name(struct name_list *list, const char *name) { + const struct name_list *s; + for (s = list; s; s = s->next) { + if (!strcmp(s->name, name)) { + return true; + } + } + return false; +} + +bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { + if (regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0 || regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0) { + return true; + } + return false; +} diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h new file mode 100644 index 00000000..c5e81dc1 --- /dev/null +++ b/plugins/check_disk.d/utils_disk.h @@ -0,0 +1,48 @@ +/* Header file for utils_disk */ + +#include "mountlist.h" +#include "utils_base.h" +#include "regex.h" + +struct name_list { + char *name; + struct name_list *next; +}; + +struct regex_list { + regex_t regex; + struct regex_list *next; +}; + +struct parameter_list { + char *name; + thresholds *freespace_bytes; + thresholds *freespace_units; + thresholds *freespace_percent; + thresholds *usedspace_bytes; + thresholds *usedspace_units; + thresholds *usedspace_percent; + thresholds *usedinodes_percent; + thresholds *freeinodes_percent; + char *group; + struct mount_entry *best_match; + struct parameter_list *name_next; + struct parameter_list *name_prev; + uintmax_t total, available, available_to_root, used, inodes_free, inodes_free_to_root, inodes_used, inodes_total; + double dfree_pct, dused_pct; + uint64_t dused_units, dfree_units, dtotal_units; + double dused_inodes_percent, dfree_inodes_percent; +}; + +void np_add_name(struct name_list **list, const char *name); +bool np_find_name(struct name_list *list, const char *name); +bool np_seen_name(struct name_list *list, const char *name); +int np_add_regex(struct regex_list **list, const char *regex, int cflags); +bool np_find_regmatch(struct regex_list *list, const char *name); +struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name); +struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name); +struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev); + +int search_parameter_list(struct parameter_list *list, const char *name); +void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact); +bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re); -- cgit v1.2.3-74-g34f1 From 8ccff07bed03046a97637a54d45a9ffe77edc235 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 14:37:02 +0100 Subject: refactor check_disk.d code a bit --- plugins/check_disk.d/utils_disk.c | 139 +++++++++++++++++++------------------- plugins/check_disk.d/utils_disk.h | 39 ++++++++--- 2 files changed, 100 insertions(+), 78 deletions(-) diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c index 2b761f5e..1d806715 100644 --- a/plugins/check_disk.d/utils_disk.c +++ b/plugins/check_disk.d/utils_disk.c @@ -63,12 +63,46 @@ int np_add_regex(struct regex_list **list, const char *regex, int cflags) { *list = new_entry; return 0; - } else { - // regcomp failed - free(new_entry); - - return regcomp_result; } + // regcomp failed + free(new_entry); + + return regcomp_result; +} + +struct parameter_list parameter_list_init(const char *name) { + struct parameter_list result = { + .name = strdup(name), + .best_match = NULL, + + .name_next = NULL, + .name_prev = NULL, + + .freespace_units = NULL, + .freespace_percent = NULL, + .usedspace_units = NULL, + .usedspace_percent = NULL, + .usedinodes_percent = NULL, + .freeinodes_percent = NULL, + + .group = NULL, + .dfree_pct = -1, + .dused_pct = -1, + .total = 0, + .available = 0, + .available_to_root = 0, + .used = 0, + .dused_units = 0, + .dfree_units = 0, + .dtotal_units = 0, + .inodes_total = 0, + .inodes_free = 0, + .inodes_free_to_root = 0, + .inodes_used = 0, + .dused_inodes_percent = 0, + .dfree_inodes_percent = 0, + }; + return result; } /* Initialises a new parameter at the end of list */ @@ -76,36 +110,8 @@ struct parameter_list *np_add_parameter(struct parameter_list **list, const char struct parameter_list *current = *list; struct parameter_list *new_path; new_path = (struct parameter_list *)malloc(sizeof *new_path); - new_path->name = (char *)malloc(strlen(name) + 1); - new_path->best_match = NULL; - new_path->name_next = NULL; - new_path->name_prev = NULL; - new_path->freespace_bytes = NULL; - new_path->freespace_units = NULL; - new_path->freespace_percent = NULL; - new_path->usedspace_bytes = NULL; - new_path->usedspace_units = NULL; - new_path->usedspace_percent = NULL; - new_path->usedinodes_percent = NULL; - new_path->freeinodes_percent = NULL; - new_path->group = NULL; - new_path->dfree_pct = -1; - new_path->dused_pct = -1; - new_path->total = 0; - new_path->available = 0; - new_path->available_to_root = 0; - new_path->used = 0; - new_path->dused_units = 0; - new_path->dfree_units = 0; - new_path->dtotal_units = 0; - new_path->inodes_total = 0; - new_path->inodes_free = 0; - new_path->inodes_free_to_root = 0; - new_path->inodes_used = 0; - new_path->dused_inodes_percent = 0; - new_path->dfree_inodes_percent = 0; - - strcpy(new_path->name, name); + + *new_path = parameter_list_init(name); if (current == NULL) { *list = new_path; @@ -125,18 +131,22 @@ struct parameter_list *np_del_parameter(struct parameter_list *item, struct para if (item == NULL) { return NULL; } + struct parameter_list *next; - if (item->name_next) + if (item->name_next) { next = item->name_next; - else + } else { next = NULL; + } - if (next) + if (next) { next->name_prev = prev; + } - if (prev) + if (prev) { prev->name_next = next; + } if (item->name) { free(item->name); @@ -148,43 +158,42 @@ struct parameter_list *np_del_parameter(struct parameter_list *item, struct para /* returns a pointer to the struct found in the list */ struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name) { - struct parameter_list *temp_list; - for (temp_list = list; temp_list; temp_list = temp_list->name_next) { - if (!strcmp(temp_list->name, name)) + for (struct parameter_list *temp_list = list; temp_list; temp_list = temp_list->name_next) { + if (!strcmp(temp_list->name, name)) { return temp_list; + } } return NULL; } void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact) { - struct parameter_list *d; - for (d = desired; d; d = d->name_next) { + for (struct parameter_list *d = desired; d; d = d->name_next) { if (!d->best_match) { - struct mount_entry *me; + struct mount_entry *mount_entry; size_t name_len = strlen(d->name); size_t best_match_len = 0; struct mount_entry *best_match = NULL; struct fs_usage fsp; /* set best match if path name exactly matches a mounted device name */ - for (me = mount_list; me; me = me->me_next) { - if (strcmp(me->me_devname, d->name) == 0) { - if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { - best_match = me; + for (mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { + if (strcmp(mount_entry->me_devname, d->name) == 0) { + if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) { + best_match = mount_entry; } } } /* set best match by directory name if no match was found by devname */ if (!best_match) { - for (me = mount_list; me; me = me->me_next) { - size_t len = strlen(me->me_mountdir); - if ((!exact && - (best_match_len <= len && len <= name_len && (len == 1 || strncmp(me->me_mountdir, d->name, len) == 0))) || - (exact && strcmp(me->me_mountdir, d->name) == 0)) { - if (get_fs_usage(me->me_mountdir, me->me_devname, &fsp) >= 0) { - best_match = me; + for (mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { + size_t len = strlen(mount_entry->me_mountdir); + if ((!exact && (best_match_len <= len && len <= name_len && + (len == 1 || strncmp(mount_entry->me_mountdir, d->name, len) == 0))) || + (exact && strcmp(mount_entry->me_mountdir, d->name) == 0)) { + if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) { + best_match = mount_entry; best_match_len = len; } } @@ -202,12 +211,10 @@ void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount /* Returns true if name is in list */ bool np_find_name(struct name_list *list, const char *name) { - const struct name_list *n; - if (list == NULL || name == NULL) { return false; } - for (n = list; n; n = n->next) { + for (struct name_list *n = list; n; n = n->next) { if (!strcmp(name, n->name)) { return true; } @@ -217,18 +224,16 @@ bool np_find_name(struct name_list *list, const char *name) { /* Returns true if name is in list */ bool np_find_regmatch(struct regex_list *list, const char *name) { - int len; - regmatch_t m; - if (name == NULL) { return false; } - len = strlen(name); + int len = strlen(name); for (; list; list = list->next) { /* Emulate a full match as if surrounded with ^( )$ by checking whether the match spans the whole name */ + regmatch_t m; if (!regexec(&list->regex, name, 1, &m, 0) && m.rm_so == 0 && m.rm_eo == len) { return true; } @@ -238,8 +243,7 @@ bool np_find_regmatch(struct regex_list *list, const char *name) { } bool np_seen_name(struct name_list *list, const char *name) { - const struct name_list *s; - for (s = list; s; s = s->next) { + for (struct name_list *s = list; s; s = s->next) { if (!strcmp(s->name, name)) { return true; } @@ -248,8 +252,5 @@ bool np_seen_name(struct name_list *list, const char *name) { } bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { - if (regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0 || regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0) { - return true; - } - return false; + return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0)); } diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h index c5e81dc1..1c68fed9 100644 --- a/plugins/check_disk.d/utils_disk.h +++ b/plugins/check_disk.d/utils_disk.h @@ -1,8 +1,10 @@ /* Header file for utils_disk */ -#include "mountlist.h" +#include "../../config.h" +#include "../../gl/mountlist.h" #include "utils_base.h" #include "regex.h" +#include struct name_list { char *name; @@ -16,22 +18,39 @@ struct regex_list { struct parameter_list { char *name; - thresholds *freespace_bytes; + char *group; + thresholds *freespace_units; thresholds *freespace_percent; - thresholds *usedspace_bytes; thresholds *usedspace_units; thresholds *usedspace_percent; + thresholds *usedinodes_percent; thresholds *freeinodes_percent; - char *group; + struct mount_entry *best_match; + + uintmax_t total; + uintmax_t available; + uintmax_t available_to_root; + uintmax_t used; + uintmax_t inodes_free; + uintmax_t inodes_free_to_root; + uintmax_t inodes_used; + uintmax_t inodes_total; + + double dfree_pct; + double dused_pct; + + uint64_t dused_units; + uint64_t dfree_units; + uint64_t dtotal_units; + + double dused_inodes_percent; + double dfree_inodes_percent; + struct parameter_list *name_next; struct parameter_list *name_prev; - uintmax_t total, available, available_to_root, used, inodes_free, inodes_free_to_root, inodes_used, inodes_total; - double dfree_pct, dused_pct; - uint64_t dused_units, dfree_units, dtotal_units; - double dused_inodes_percent, dfree_inodes_percent; }; void np_add_name(struct name_list **list, const char *name); @@ -39,10 +58,12 @@ bool np_find_name(struct name_list *list, const char *name); bool np_seen_name(struct name_list *list, const char *name); int np_add_regex(struct regex_list **list, const char *regex, int cflags); bool np_find_regmatch(struct regex_list *list, const char *name); + struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name); struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name); struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev); +struct parameter_list parameter_list_init(const char *); int search_parameter_list(struct parameter_list *list, const char *name); void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact); -bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re); +bool np_regex_match_mount_entry(struct mount_entry *, regex_t *); -- cgit v1.2.3-74-g34f1 From 29d946b9b516662a0f625b7d229ee41962cac264 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 14:37:49 +0100 Subject: Refactor check_disk, no more global variables --- plugins/check_disk.c | 530 +++++++++++++++++++++--------------------- plugins/check_disk.d/config.h | 92 ++++++++ 2 files changed, 358 insertions(+), 264 deletions(-) create mode 100644 plugins/check_disk.d/config.h diff --git a/plugins/check_disk.c b/plugins/check_disk.c index f67f3d57..2df89aa3 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -43,13 +43,15 @@ const char *email = "devel@monitoring-plugins.org"; #endif #include -#include "popen.h" -#include "utils.h" -#include "utils_disk.h" #include -#include "fsusage.h" -#include "mountlist.h" +#include #include +#include "./popen.h" +#include "./utils.h" +#include "./check_disk.d/utils_disk.h" +#include "../gl/fsusage.h" +#include "../gl/mountlist.h" +#include "check_disk.d/config.h" #if HAVE_LIMITS_H # include @@ -63,27 +65,6 @@ const char *email = "devel@monitoring-plugins.org"; # define ERROR -1 #endif -/* If nonzero, show only local filesystems. */ -static bool show_local_fs = false; - -/* If nonzero, show only local filesystems but call stat() on remote ones. */ -static bool stat_remote_fs = false; - -/* Linked list of filesystem types to omit. - If the list is empty, don't exclude any types. */ -static struct regex_list *fs_exclude_list = NULL; - -/* Linked list of filesystem types to check. - If the list is empty, include all types. */ -static struct regex_list *fs_include_list; - -static struct name_list *dp_exclude_list; - -static struct parameter_list *path_select_list = NULL; - -/* Linked list of mounted filesystems. */ -static struct mount_entry *mount_list; - /* For long options that have no equivalent short option, use a non-character as a pseudo short option, starting with CHAR_MAX + 1. */ enum { @@ -96,40 +77,27 @@ enum { # pragma alloca #endif -static int process_arguments(int /*argc*/, char ** /*argv*/); -static void set_all_thresholds(struct parameter_list *path); +typedef struct { + int errorcode; + check_disk_config config; +} check_disk_config_wrapper; +static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); +static void set_all_thresholds(struct parameter_list *path, char * /*warn_freespace_units*/, char * /*crit_freespace_units*/, + char * /*warn_freespace_percent*/, char * /*crit_freespace_percent*/, char * /*warn_usedspace_units*/, + char * /*crit_usedspace_units*/, char * /*warn_usedspace_percent*/, char * /*crit_usedspace_percent*/, + char * /*warn_usedinodes_percent*/, char * /*crit_usedinodes_percent*/, char * /*warn_freeinodes_percent*/, + char * /*crit_freeinodes_percent*/); static void print_help(void); void print_usage(void); static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/); -static bool stat_path(struct parameter_list * /*parameters*/); -static void get_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp); -static void get_path_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp); +static bool stat_path(struct parameter_list * /*parameters*/, bool /*ignore_missing*/); +static void get_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp, bool /*ignore_missing*/, + bool /*freespace_ignore_reserved*/, uintmax_t /*mult*/, struct parameter_list * /*path_select_list*/, + struct name_list * /*seen*/); +static void get_path_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp, bool /*freespace_ignore_reserved*/, + uintmax_t /*mult*/, struct name_list * /*seen*/); -static char *units; -static uintmax_t mult = 1024 * 1024; static int verbose = 0; -static bool erronly = false; -static bool display_mntp = false; -static bool exact_match = false; -static bool ignore_missing = false; -static bool freespace_ignore_reserved = false; -static bool display_inodes_perfdata = false; -static char *warn_freespace_units = NULL; -static char *crit_freespace_units = NULL; -static char *warn_freespace_percent = NULL; -static char *crit_freespace_percent = NULL; -static char *warn_usedspace_units = NULL; -static char *crit_usedspace_units = NULL; -static char *warn_usedspace_percent = NULL; -static char *crit_usedspace_percent = NULL; -static char *warn_usedinodes_percent = NULL; -static char *crit_usedinodes_percent = NULL; -static char *warn_freeinodes_percent = NULL; -static char *crit_freeinodes_percent = NULL; -static bool path_selected = false; -static bool path_ignored = false; -static char *group = NULL; -static struct name_list *seen = NULL; int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -140,74 +108,78 @@ int main(int argc, char **argv) { char mountdir[32]; #endif - mount_list = read_file_system_list(false); - /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) == ERROR) { + check_disk_config_wrapper tmp_config = process_arguments(argc, argv); + if (tmp_config.errorcode == ERROR) { usage4(_("Could not parse arguments")); } + check_disk_config config = tmp_config.config; + /* If a list of paths has not been selected, find entire mount list and create list of paths */ - if (!path_selected && !path_ignored) { - for (struct mount_entry *me = mount_list; me; me = me->me_next) { + if (!config.path_selected && !config.path_ignored) { + for (struct mount_entry *me = config.mount_list; me; me = me->me_next) { struct parameter_list *path = NULL; - if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { - path = np_add_parameter(&path_select_list, me->me_mountdir); + if (!(path = np_find_parameter(config.path_select_list, me->me_mountdir))) { + path = np_add_parameter(&config.path_select_list, me->me_mountdir); } path->best_match = me; - path->group = group; - set_all_thresholds(path); + path->group = config.group; + set_all_thresholds(path, config.warn_freespace_units, config.crit_freespace_units, config.warn_freespace_percent, + config.crit_freespace_percent, config.warn_usedspace_units, config.crit_usedspace_units, + config.warn_usedspace_percent, config.crit_usedspace_percent, config.warn_usedinodes_percent, + config.crit_usedinodes_percent, config.warn_freeinodes_percent, config.crit_freeinodes_percent); } } - if (!path_ignored) { - np_set_best_match(path_select_list, mount_list, exact_match); + if (!config.path_ignored) { + np_set_best_match(config.path_select_list, config.mount_list, config.exact_match); } /* Error if no match found for specified paths */ - struct parameter_list *temp_list = path_select_list; + struct parameter_list *temp_list = config.path_select_list; char *ignored = strdup(""); - while (path_select_list) { - if (!path_select_list->best_match && ignore_missing) { + while (config.path_select_list) { + if (!config.path_select_list->best_match && config.ignore_missing) { /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ - if (path_select_list == temp_list) { - temp_list = path_select_list->name_next; + if (config.path_select_list == temp_list) { + temp_list = config.path_select_list->name_next; } /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */ - xasprintf(&ignored, "%s %s;", ignored, path_select_list->name); + xasprintf(&ignored, "%s %s;", ignored, config.path_select_list->name); /* Delete the path from the list so that it is not stat-checked later in the code. */ - path_select_list = np_del_parameter(path_select_list, path_select_list->name_prev); - } else if (!path_select_list->best_match) { + config.path_select_list = np_del_parameter(config.path_select_list, config.path_select_list->name_prev); + } else if (!config.path_select_list->best_match) { /* Without --ignore-missing option, exit with Critical state. */ - die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), path_select_list->name); + die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), config.path_select_list->name); } else { /* Continue jumping through the list */ - path_select_list = path_select_list->name_next; + config.path_select_list = config.path_select_list->name_next; } } - path_select_list = temp_list; + config.path_select_list = temp_list; - int result = STATE_UNKNOWN; - if (!path_select_list && ignore_missing) { + mp_state_enum result = STATE_UNKNOWN; + if (!config.path_select_list && config.ignore_missing) { result = STATE_OK; if (verbose >= 2) { printf("None of the provided paths were found\n"); } } - mp_state_enum disk_result = STATE_UNKNOWN; + mp_state_enum filesystem_result = STATE_UNKNOWN; char *perf = strdup(""); char *perf_ilabel = strdup(""); char *output = strdup(""); struct parameter_list *path = NULL; /* Process for every path in list */ - for (path = path_select_list; path; path = path->name_next) { + for (path = config.path_select_list; path; path = path->name_next) { if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) { printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, path->freespace_percent->critical->end); @@ -217,8 +189,8 @@ int main(int argc, char **argv) { printf("Group of %s: %s\n", path->name, path->group); } - // reset disk result - disk_result = STATE_UNKNOWN; + // reset filesystem result + filesystem_result = STATE_UNKNOWN; struct mount_entry *mount_entry = path->best_match; @@ -238,16 +210,16 @@ int main(int argc, char **argv) { /* Filters */ /* Remove filesystems already seen */ - if (np_seen_name(seen, mount_entry->me_mountdir)) { + if (np_seen_name(config.seen, mount_entry->me_mountdir)) { continue; } - np_add_name(&seen, mount_entry->me_mountdir); + np_add_name(&config.seen, mount_entry->me_mountdir); if (path->group == NULL) { /* Skip remote filesystems if we're not interested in them */ - if (mount_entry->me_remote && show_local_fs) { - if (stat_remote_fs) { - if (!stat_path(path) && ignore_missing) { + if (mount_entry->me_remote && config.show_local_fs) { + if (config.stat_remote_fs) { + if (!stat_path(path, config.ignore_missing) && config.ignore_missing) { result = STATE_OK; xasprintf(&ignored, "%s %s;", ignored, path->name); } @@ -255,22 +227,22 @@ int main(int argc, char **argv) { continue; /* Skip pseudo fs's if we haven't asked for all fs's */ } - if (fs_exclude_list && np_find_regmatch(fs_exclude_list, mount_entry->me_type)) { + if (config.fs_exclude_list && np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) { continue; /* Skip excluded fs's */ } - if (dp_exclude_list && - (np_find_name(dp_exclude_list, mount_entry->me_devname) || np_find_name(dp_exclude_list, mount_entry->me_mountdir))) { + if (config.device_path_exclude_list && (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) || + np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) { continue; /* Skip not included fstypes */ } - if (fs_include_list && !np_find_regmatch(fs_include_list, mount_entry->me_type)) { + if (config.fs_include_list && !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) { continue; } } - if (!stat_path(path)) { - if (ignore_missing) { + if (!stat_path(path, config.ignore_missing)) { + if (config.ignore_missing) { result = STATE_OK; xasprintf(&ignored, "%s %s;", ignored, path->name); } @@ -281,13 +253,14 @@ int main(int argc, char **argv) { get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp); if (fsp.fsu_blocks && strcmp("none", mount_entry->me_mountdir)) { - get_stats(path, &fsp); + get_stats(path, &fsp, config.ignore_missing, config.freespace_ignore_reserved, config.mult, config.path_select_list, + config.seen); if (verbose >= 3) { printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f " "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n", mount_entry->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units, - path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, mult); + path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, config.mult); } /* Threshold comparisons */ @@ -296,39 +269,39 @@ int main(int argc, char **argv) { if (verbose >= 3) { printf("Freespace_units result=%d\n", temp_result); } - disk_result = max_state(disk_result, temp_result); + filesystem_result = max_state(filesystem_result, temp_result); temp_result = get_status(path->dfree_pct, path->freespace_percent); if (verbose >= 3) { printf("Freespace%% result=%d\n", temp_result); } - disk_result = max_state(disk_result, temp_result); + filesystem_result = max_state(filesystem_result, temp_result); temp_result = get_status(path->dused_units, path->usedspace_units); if (verbose >= 3) { printf("Usedspace_units result=%d\n", temp_result); } - disk_result = max_state(disk_result, temp_result); + filesystem_result = max_state(filesystem_result, temp_result); temp_result = get_status(path->dused_pct, path->usedspace_percent); if (verbose >= 3) { printf("Usedspace_percent result=%d\n", temp_result); } - disk_result = max_state(disk_result, temp_result); + filesystem_result = max_state(filesystem_result, temp_result); temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent); if (verbose >= 3) { printf("Usedinodes_percent result=%d\n", temp_result); } - disk_result = max_state(disk_result, temp_result); + filesystem_result = max_state(filesystem_result, temp_result); temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent); if (verbose >= 3) { printf("Freeinodes_percent result=%d\n", temp_result); } - disk_result = max_state(disk_result, temp_result); + filesystem_result = max_state(filesystem_result, temp_result); - result = max_state(result, disk_result); + result = max_state(result, filesystem_result); /* What a mess of units. The output shows free space, the perf data shows used space. Yikes! Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf @@ -339,31 +312,32 @@ int main(int argc, char **argv) { uint64_t warning_high_tide = UINT64_MAX; if (path->freespace_units->warning != NULL) { - warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * mult; + warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * config.mult; } if (path->freespace_percent->warning != NULL) { - warning_high_tide = - min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * (path->dtotal_units * mult))); + warning_high_tide = min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * + (path->dtotal_units * config.mult))); } uint64_t critical_high_tide = UINT64_MAX; if (path->freespace_units->critical != NULL) { - critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * mult; + critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * config.mult; } if (path->freespace_percent->critical != NULL) { - critical_high_tide = - min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * (path->dtotal_units * mult))); + critical_high_tide = min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * + (path->dtotal_units * config.mult))); } /* Nb: *_high_tide are unset when == UINT64_MAX */ xasprintf(&perf, "%s %s", perf, - perfdata_uint64((!strcmp(mount_entry->me_mountdir, "none") || display_mntp) ? mount_entry->me_devname - : mount_entry->me_mountdir, - path->dused_units * mult, "B", (warning_high_tide != UINT64_MAX), warning_high_tide, - (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, path->dtotal_units * mult)); + perfdata_uint64((!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname + : mount_entry->me_mountdir, + path->dused_units * config.mult, "B", (warning_high_tide != UINT64_MAX), warning_high_tide, + (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, + path->dtotal_units * config.mult)); - if (display_inodes_perfdata) { + if (config.display_inodes_perfdata) { /* *_high_tide must be reinitialized at each run */ warning_high_tide = UINT64_MAX; critical_high_tide = UINT64_MAX; @@ -378,43 +352,46 @@ int main(int argc, char **argv) { } xasprintf(&perf_ilabel, "%s (inodes)", - (!strcmp(mount_entry->me_mountdir, "none") || display_mntp) ? mount_entry->me_devname : mount_entry->me_mountdir); + (!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname + : mount_entry->me_mountdir); /* Nb: *_high_tide are unset when == UINT64_MAX */ xasprintf(&perf, "%s %s", perf, perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX), warning_high_tide, (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, path->inodes_total)); } - if (disk_result == STATE_OK && erronly && !verbose) { + if (filesystem_result == STATE_OK && config.erronly && !verbose) { continue; } char *flag_header = NULL; - if (disk_result && verbose >= 1) { - xasprintf(&flag_header, " %s [", state_text(disk_result)); + if (filesystem_result && verbose >= 1) { + xasprintf(&flag_header, " %s [", state_text(filesystem_result)); } else { xasprintf(&flag_header, ""); } xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, - (!strcmp(mount_entry->me_mountdir, "none") || display_mntp) ? mount_entry->me_devname : mount_entry->me_mountdir, - path->dfree_units, units, path->dfree_pct); + (!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname + : mount_entry->me_mountdir, + path->dfree_units, config.units, path->dfree_pct); if (path->dused_inodes_percent < 0) { - xasprintf(&output, "%s inode=-)%s;", output, (disk_result ? "]" : "")); + xasprintf(&output, "%s inode=-)%s;", output, (filesystem_result ? "]" : "")); } else { - xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, ((disk_result && verbose >= 1) ? "]" : "")); + xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, + ((filesystem_result && verbose >= 1) ? "]" : "")); } free(flag_header); } } char *preamble = " - free space:"; - if (strcmp(output, "") == 0 && !erronly) { + if (strcmp(output, "") == 0 && !config.erronly) { preamble = ""; xasprintf(&output, " - No disks were found for provided parameters"); } char *ignored_preamble = " - ignored paths:"; - printf("DISK %s%s%s%s%s|%s\n", state_text(result), ((erronly && result == STATE_OK)) ? "" : preamble, output, + printf("DISK %s%s%s%s%s|%s\n", state_text(result), (config.erronly && (result == STATE_OK)) ? "" : preamble, output, (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf); return result; } @@ -428,9 +405,16 @@ double calculate_percent(uintmax_t value, uintmax_t total) { } /* process command-line arguments */ -int process_arguments(int argc, char **argv) { +check_disk_config_wrapper process_arguments(int argc, char **argv) { + + check_disk_config_wrapper result = { + .errorcode = OK, + .config = check_disk_config_init(), + }; + if (argc < 2) { - return ERROR; + result.errorcode = ERROR; + return result; } static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, @@ -480,8 +464,9 @@ int process_arguments(int argc, char **argv) { int cflags = REG_NOSUB | REG_EXTENDED; int default_cflags = cflags; + result.config.mount_list = read_file_system_list(false); - np_add_regex(&fs_exclude_list, "iso9660", REG_EXTENDED); + np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED); while (true) { int option = 0; @@ -508,15 +493,15 @@ int process_arguments(int argc, char **argv) { if (strstr(optarg, "%")) { if (*optarg == '@') { - warn_freespace_percent = optarg; + result.config.warn_freespace_percent = optarg; } else { - xasprintf(&warn_freespace_percent, "@%s", optarg); + xasprintf(&result.config.warn_freespace_percent, "@%s", optarg); } } else { if (*optarg == '@') { - warn_freespace_units = optarg; + result.config.warn_freespace_units = optarg; } else { - xasprintf(&warn_freespace_units, "@%s", optarg); + xasprintf(&result.config.warn_freespace_units, "@%s", optarg); } } break; @@ -533,149 +518,149 @@ int process_arguments(int argc, char **argv) { if (strstr(optarg, "%")) { if (*optarg == '@') { - crit_freespace_percent = optarg; + result.config.crit_freespace_percent = optarg; } else { - xasprintf(&crit_freespace_percent, "@%s", optarg); + xasprintf(&result.config.crit_freespace_percent, "@%s", optarg); } } else { if (*optarg == '@') { - crit_freespace_units = optarg; + result.config.crit_freespace_units = optarg; } else { - xasprintf(&crit_freespace_units, "@%s", optarg); + xasprintf(&result.config.crit_freespace_units, "@%s", optarg); } } break; case 'W': /* warning inode threshold */ if (*optarg == '@') { - warn_freeinodes_percent = optarg; + result.config.warn_freeinodes_percent = optarg; } else { - xasprintf(&warn_freeinodes_percent, "@%s", optarg); + xasprintf(&result.config.warn_freeinodes_percent, "@%s", optarg); } break; case 'K': /* critical inode threshold */ if (*optarg == '@') { - crit_freeinodes_percent = optarg; + result.config.crit_freeinodes_percent = optarg; } else { - xasprintf(&crit_freeinodes_percent, "@%s", optarg); + xasprintf(&result.config.crit_freeinodes_percent, "@%s", optarg); } break; case 'u': - if (units) { - free(units); - } + free(result.config.units); if (!strcasecmp(optarg, "bytes")) { - mult = (uintmax_t)1; - units = strdup("B"); + result.config.mult = (uintmax_t)1; + result.config.units = strdup("B"); } else if (!strcmp(optarg, "KiB")) { - mult = (uintmax_t)1024; - units = strdup("KiB"); + result.config.mult = (uintmax_t)1024; + result.config.units = strdup("KiB"); } else if (!strcmp(optarg, "kB")) { - mult = (uintmax_t)1000; - units = strdup("kB"); + result.config.mult = (uintmax_t)1000; + result.config.units = strdup("kB"); } else if (!strcmp(optarg, "MiB")) { - mult = (uintmax_t)1024 * 1024; - units = strdup("MiB"); + result.config.mult = (uintmax_t)1024 * 1024; + result.config.units = strdup("MiB"); } else if (!strcmp(optarg, "MB")) { - mult = (uintmax_t)1000 * 1000; - units = strdup("MB"); + result.config.mult = (uintmax_t)1000 * 1000; + result.config.units = strdup("MB"); } else if (!strcmp(optarg, "GiB")) { - mult = (uintmax_t)1024 * 1024 * 1024; - units = strdup("GiB"); + result.config.mult = (uintmax_t)1024 * 1024 * 1024; + result.config.units = strdup("GiB"); } else if (!strcmp(optarg, "GB")) { - mult = (uintmax_t)1000 * 1000 * 1000; - units = strdup("GB"); + result.config.mult = (uintmax_t)1000 * 1000 * 1000; + result.config.units = strdup("GB"); } else if (!strcmp(optarg, "TiB")) { - mult = (uintmax_t)1024 * 1024 * 1024 * 1024; - units = strdup("TiB"); + result.config.mult = (uintmax_t)1024 * 1024 * 1024 * 1024; + result.config.units = strdup("TiB"); } else if (!strcmp(optarg, "TB")) { - mult = (uintmax_t)1000 * 1000 * 1000 * 1000; - units = strdup("TB"); + result.config.mult = (uintmax_t)1000 * 1000 * 1000 * 1000; + result.config.units = strdup("TB"); } else if (!strcmp(optarg, "PiB")) { - mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; - units = strdup("PiB"); + result.config.mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; + result.config.units = strdup("PiB"); } else if (!strcmp(optarg, "PB")) { - mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; - units = strdup("PB"); + result.config.mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; + result.config.units = strdup("PB"); } else { die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); } - if (units == NULL) { + if (result.config.units == NULL) { die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units"); } break; case 'k': /* display mountpoint */ - mult = 1024; - if (units) { - free(units); - } - units = strdup("kiB"); + result.config.mult = 1024; + free(result.config.units); + result.config.units = strdup("kiB"); break; case 'm': /* display mountpoint */ - mult = 1024 * 1024; - if (units) { - free(units); - } - units = strdup("MiB"); + result.config.mult = 1024 * 1024; + free(result.config.units); + result.config.units = strdup("MiB"); break; case 'L': - stat_remote_fs = true; + result.config.stat_remote_fs = true; /* fallthrough */ case 'l': - show_local_fs = true; + result.config.show_local_fs = true; break; case 'P': - display_inodes_perfdata = true; + result.config.display_inodes_perfdata = true; break; case 'p': /* select path */ { - if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || - warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || - warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { + if (!(result.config.warn_freespace_units || result.config.crit_freespace_units || result.config.warn_freespace_percent || + result.config.crit_freespace_percent || result.config.warn_usedspace_units || result.config.crit_usedspace_units || + result.config.warn_usedspace_percent || result.config.crit_usedspace_percent || result.config.warn_usedinodes_percent || + result.config.crit_usedinodes_percent || result.config.warn_freeinodes_percent || + result.config.crit_freeinodes_percent)) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); } /* add parameter if not found. overwrite thresholds if path has already been added */ struct parameter_list *se; - if (!(se = np_find_parameter(path_select_list, optarg))) { - se = np_add_parameter(&path_select_list, optarg); + if (!(se = np_find_parameter(result.config.path_select_list, optarg))) { + se = np_add_parameter(&result.config.path_select_list, optarg); struct stat stat_buf = {}; - if (stat(optarg, &stat_buf) && ignore_missing) { - path_ignored = true; + if (stat(optarg, &stat_buf) && result.config.ignore_missing) { + result.config.path_ignored = true; break; } } - se->group = group; - set_all_thresholds(se); + se->group = result.config.group; + set_all_thresholds( + se, result.config.warn_freespace_units, result.config.crit_freespace_units, result.config.warn_freespace_percent, + result.config.crit_freespace_percent, result.config.warn_usedspace_units, result.config.crit_usedspace_units, + result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, result.config.warn_usedinodes_percent, + result.config.crit_usedinodes_percent, result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); /* With autofs, it is required to stat() the path before re-populating the mount_list */ - if (!stat_path(se)) { + if (!stat_path(se, result.config.ignore_missing)) { break; } /* NB: We can't free the old mount_list "just like that": both list pointers and struct * pointers are copied around. One of the reason it wasn't done yet is that other parts * of check_disk need the same kind of cleanup so it'd better be done as a whole */ - mount_list = read_file_system_list(false); - np_set_best_match(se, mount_list, exact_match); + result.config.mount_list = read_file_system_list(false); + np_set_best_match(se, result.config.mount_list, result.config.exact_match); - path_selected = true; + result.config.path_selected = true; } break; case 'x': /* exclude path or partition */ - np_add_name(&dp_exclude_list, optarg); + np_add_name(&result.config.device_path_exclude_list, optarg); break; case 'X': /* exclude file system type */ { - int err = np_add_regex(&fs_exclude_list, optarg, REG_EXTENDED); + int err = np_add_regex(&result.config.fs_exclude_list, optarg, REG_EXTENDED); if (err != 0) { char errbuf[MAX_INPUT_BUFFER]; - regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); + regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } break; case 'N': /* include file system type */ - err = np_add_regex(&fs_include_list, optarg, REG_EXTENDED); + err = np_add_regex(&result.config.fs_include_list, optarg, REG_EXTENDED); if (err != 0) { char errbuf[MAX_INPUT_BUFFER]; - regerror(err, &fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); + regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } } break; @@ -684,31 +669,31 @@ int process_arguments(int argc, char **argv) { break; case 'q': /* TODO: this function should eventually go away (removed 2007-09-20) */ /* verbose--; **replaced by line below**. -q was only a broken way of implementing -e */ - erronly = true; + result.config.erronly = true; break; case 'e': - erronly = true; + result.config.erronly = true; break; case 'E': - if (path_selected) { + if (result.config.path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); } - exact_match = true; + result.config.exact_match = true; break; case 'f': - freespace_ignore_reserved = true; + result.config.freespace_ignore_reserved = true; break; case 'g': - if (path_selected) { + if (result.config.path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); } - group = optarg; + result.config.group = optarg; break; case 'I': cflags |= REG_ICASE; // Intentional fallthrough case 'i': { - if (!path_selected) { + if (!result.config.path_selected) { die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); } @@ -720,7 +705,7 @@ int process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } - struct parameter_list *temp_list = path_select_list; + struct parameter_list *temp_list = result.config.path_select_list; struct parameter_list *previous = NULL; while (temp_list) { if (temp_list->best_match) { @@ -733,7 +718,7 @@ int process_arguments(int argc, char **argv) { temp_list = np_del_parameter(temp_list, previous); /* pointer to first element needs to be updated if first item gets deleted */ if (previous == NULL) { - path_select_list = temp_list; + result.config.path_select_list = temp_list; } } else { previous = temp_list; @@ -748,7 +733,7 @@ int process_arguments(int argc, char **argv) { cflags = default_cflags; } break; case 'n': - ignore_missing = true; + result.config.ignore_missing = true; break; case 'A': optarg = strdup(".*"); @@ -757,9 +742,11 @@ int process_arguments(int argc, char **argv) { cflags |= REG_ICASE; // Intentional fallthrough case 'r': { - if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || - warn_usedspace_units || crit_usedspace_units || warn_usedspace_percent || crit_usedspace_percent || - warn_usedinodes_percent || crit_usedinodes_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { + if (!(result.config.warn_freespace_units || result.config.crit_freespace_units || result.config.warn_freespace_percent || + result.config.crit_freespace_percent || result.config.warn_usedspace_units || result.config.crit_usedspace_units || + result.config.warn_usedspace_percent || result.config.crit_usedspace_percent || result.config.warn_usedinodes_percent || + result.config.crit_usedinodes_percent || result.config.warn_freeinodes_percent || + result.config.crit_freeinodes_percent)) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); } @@ -773,7 +760,7 @@ int process_arguments(int argc, char **argv) { } bool found = false; - for (struct mount_entry *me = mount_list; me; me = me->me_next) { + for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { if (np_regex_match_mount_entry(me, ®ex)) { found = true; if (verbose >= 3) { @@ -782,60 +769,69 @@ int process_arguments(int argc, char **argv) { /* add parameter if not found. overwrite thresholds if path has already been added */ struct parameter_list *se = NULL; - if (!(se = np_find_parameter(path_select_list, me->me_mountdir))) { - se = np_add_parameter(&path_select_list, me->me_mountdir); + if (!(se = np_find_parameter(result.config.path_select_list, me->me_mountdir))) { + se = np_add_parameter(&result.config.path_select_list, me->me_mountdir); } - se->group = group; - set_all_thresholds(se); + se->group = result.config.group; + set_all_thresholds(se, result.config.warn_freespace_units, result.config.crit_freespace_units, + result.config.warn_freespace_percent, result.config.crit_freespace_percent, + result.config.warn_usedspace_units, result.config.crit_usedspace_units, + result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, + result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, + result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); } } - if (!found && ignore_missing) { - path_ignored = true; - path_selected = true; + if (!found && result.config.ignore_missing) { + result.config.path_ignored = true; + result.config.path_selected = true; break; } if (!found) { die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); } - found = false; - path_selected = true; - np_set_best_match(path_select_list, mount_list, exact_match); + result.config.path_selected = true; + np_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); cflags = default_cflags; } break; case 'M': /* display mountpoint */ - display_mntp = true; + result.config.display_mntp = true; break; case 'C': { /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ - if (!path_selected) { + if (!result.config.path_selected) { struct parameter_list *path; - for (struct mount_entry *me = mount_list; me; me = me->me_next) { - if (!(path = np_find_parameter(path_select_list, me->me_mountdir))) { - path = np_add_parameter(&path_select_list, me->me_mountdir); + for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { + if (!(path = np_find_parameter(result.config.path_select_list, me->me_mountdir))) { + path = np_add_parameter(&result.config.path_select_list, me->me_mountdir); } path->best_match = me; - path->group = group; - set_all_thresholds(path); + path->group = result.config.group; + set_all_thresholds(path, result.config.warn_freespace_units, result.config.crit_freespace_units, + result.config.warn_freespace_percent, result.config.crit_freespace_percent, + result.config.warn_usedspace_units, result.config.crit_usedspace_units, + result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, + result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, + result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); } } - warn_freespace_units = NULL; - crit_freespace_units = NULL; - warn_usedspace_units = NULL; - crit_usedspace_units = NULL; - warn_freespace_percent = NULL; - crit_freespace_percent = NULL; - warn_usedspace_percent = NULL; - crit_usedspace_percent = NULL; - warn_usedinodes_percent = NULL; - crit_usedinodes_percent = NULL; - warn_freeinodes_percent = NULL; - crit_freeinodes_percent = NULL; - - path_selected = false; - group = NULL; + result.config.warn_freespace_units = NULL; + result.config.crit_freespace_units = NULL; + result.config.warn_usedspace_units = NULL; + result.config.crit_usedspace_units = NULL; + result.config.warn_freespace_percent = NULL; + result.config.crit_freespace_percent = NULL; + result.config.warn_usedspace_percent = NULL; + result.config.crit_usedspace_percent = NULL; + result.config.warn_usedinodes_percent = NULL; + result.config.crit_usedinodes_percent = NULL; + result.config.warn_freeinodes_percent = NULL; + result.config.crit_freeinodes_percent = NULL; + + result.config.path_selected = false; + result.config.group = NULL; } break; case 'V': /* version */ print_revision(progname, NP_VERSION); @@ -847,45 +843,49 @@ int process_arguments(int argc, char **argv) { usage(_("Unknown argument")); } } - if (verbose > 0) { - printf("ping\n"); - } /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ int index = optind; - if (warn_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (result.config.warn_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { if (verbose > 0) { printf("Got an positional warn threshold: %s\n", argv[index]); } - warn_usedspace_percent = argv[index++]; + result.config.warn_usedspace_percent = argv[index++]; } - if (crit_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (result.config.crit_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { if (verbose > 0) { printf("Got an positional crit threshold: %s\n", argv[index]); } - crit_usedspace_percent = argv[index++]; + result.config.crit_usedspace_percent = argv[index++]; } if (argc > index) { if (verbose > 0) { printf("Got an positional filesystem: %s\n", argv[index]); } - struct parameter_list *se = np_add_parameter(&path_select_list, strdup(argv[index++])); - path_selected = true; - set_all_thresholds(se); + struct parameter_list *se = np_add_parameter(&result.config.path_select_list, strdup(argv[index++])); + result.config.path_selected = true; + set_all_thresholds(se, result.config.warn_freespace_units, result.config.crit_freespace_units, result.config.warn_freespace_percent, + result.config.crit_freespace_percent, result.config.warn_usedspace_units, result.config.crit_usedspace_units, + result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, + result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, + result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); } - if (units == NULL) { - units = strdup("MiB"); - mult = (uintmax_t)1024 * 1024; + if (result.config.units == NULL) { + result.config.units = strdup("MiB"); + result.config.mult = (uintmax_t)1024 * 1024; } - return 0; + return result; } -void set_all_thresholds(struct parameter_list *path) { +void set_all_thresholds(struct parameter_list *path, char *warn_freespace_units, char *crit_freespace_units, char *warn_freespace_percent, + char *crit_freespace_percent, char *warn_usedspace_units, char *crit_usedspace_units, char *warn_usedspace_percent, + char *crit_usedspace_percent, char *warn_usedinodes_percent, char *crit_usedinodes_percent, + char *warn_freeinodes_percent, char *crit_freeinodes_percent) { if (path->freespace_units != NULL) { free(path->freespace_units); } @@ -1022,7 +1022,7 @@ void print_usage(void) { printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); } -bool stat_path(struct parameter_list *parameters) { +bool stat_path(struct parameter_list *parameters, bool ignore_missing) { /* Stat entry to check that dir exists and is accessible */ if (verbose >= 3) { printf("calling stat on %s\n", parameters->name); @@ -1043,12 +1043,13 @@ bool stat_path(struct parameter_list *parameters) { return true; } -void get_stats(struct parameter_list *parameters, struct fs_usage *fsp) { +void get_stats(struct parameter_list *parameters, struct fs_usage *fsp, bool ignore_missing, bool freespace_ignore_reserved, uintmax_t mult, + struct parameter_list *path_select_list, struct name_list *seen) { struct fs_usage tmpfsp; bool first = true; if (parameters->group == NULL) { - get_path_stats(parameters, fsp); + get_path_stats(parameters, fsp, freespace_ignore_reserved, mult, seen); } else { /* find all group members */ for (struct parameter_list *p_list = path_select_list; p_list; p_list = p_list->name_next) { @@ -1060,11 +1061,11 @@ void get_stats(struct parameter_list *parameters, struct fs_usage *fsp) { #endif if (p_list->group && !(strcmp(p_list->group, parameters->group))) { - if (!stat_path(p_list)) { + if (!stat_path(p_list, ignore_missing)) { continue; } get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp); - get_path_stats(p_list, &tmpfsp); + get_path_stats(p_list, &tmpfsp, freespace_ignore_reserved, mult, seen); if (verbose >= 3) { printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n", p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, @@ -1105,7 +1106,8 @@ void get_stats(struct parameter_list *parameters, struct fs_usage *fsp) { parameters->dfree_inodes_percent = 100 - parameters->dused_inodes_percent; } -void get_path_stats(struct parameter_list *parameters, struct fs_usage *fsp) { +void get_path_stats(struct parameter_list *parameters, struct fs_usage *fsp, bool freespace_ignore_reserved, uintmax_t mult, + struct name_list *seen) { parameters->available = fsp->fsu_bavail; parameters->available_to_root = fsp->fsu_bfree; parameters->used = fsp->fsu_blocks - fsp->fsu_bfree; diff --git a/plugins/check_disk.d/config.h b/plugins/check_disk.d/config.h new file mode 100644 index 00000000..d890fc1a --- /dev/null +++ b/plugins/check_disk.d/config.h @@ -0,0 +1,92 @@ +#pragma once + +#include "../../config.h" +#include +#include + +typedef struct { + // Output options + bool erronly; + bool display_mntp; + /* show only local filesystems. */ + bool show_local_fs; + /* show only local filesystems but call stat() on remote ones. */ + bool stat_remote_fs; + bool display_inodes_perfdata; + + bool exact_match; + bool ignore_missing; + bool path_ignored; + bool path_selected; + bool freespace_ignore_reserved; + + char *warn_freespace_units; + char *crit_freespace_units; + char *warn_freespace_percent; + char *crit_freespace_percent; + char *warn_usedspace_units; + char *crit_usedspace_units; + char *warn_usedspace_percent; + char *crit_usedspace_percent; + char *warn_usedinodes_percent; + char *crit_usedinodes_percent; + char *warn_freeinodes_percent; + char *crit_freeinodes_percent; + + /* Linked list of filesystem types to omit. + If the list is empty, don't exclude any types. */ + struct regex_list *fs_exclude_list; + /* Linked list of filesystem types to check. + If the list is empty, include all types. */ + struct regex_list *fs_include_list; + struct name_list *device_path_exclude_list; + struct parameter_list *path_select_list; + /* Linked list of mounted filesystems. */ + struct mount_entry *mount_list; + struct name_list *seen; + + char *units; + uintmax_t mult; + char *group; +} check_disk_config; + +check_disk_config check_disk_config_init() { + check_disk_config tmp = { + .erronly = false, + .display_mntp = false, + .show_local_fs = false, + .stat_remote_fs = false, + .display_inodes_perfdata = false, + + .exact_match = false, + .ignore_missing = false, + .path_ignored = false, + .path_selected = false, + .freespace_ignore_reserved = false, + + .warn_freespace_units = NULL, + .crit_freespace_units = NULL, + .warn_freespace_percent = NULL, + .crit_freespace_percent = NULL, + .warn_usedspace_units = NULL, + .crit_usedspace_units = NULL, + .warn_usedspace_percent = NULL, + .crit_usedspace_percent = NULL, + .warn_usedinodes_percent = NULL, + .crit_usedinodes_percent = NULL, + .warn_freeinodes_percent = NULL, + .crit_freeinodes_percent = NULL, + + .fs_exclude_list = NULL, + .fs_include_list = NULL, + .device_path_exclude_list = NULL, + .path_select_list = NULL, + .mount_list = NULL, + .seen = NULL, + + .units = NULL, + .mult = 1024 * 1024, + .group = NULL, + }; + return tmp; +} -- cgit v1.2.3-74-g34f1 From 7994b478187d38c04dd2d7d9241d237875589738 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 14:38:27 +0100 Subject: Adapt .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7f79265f..5245495e 100644 --- a/.gitignore +++ b/.gitignore @@ -153,6 +153,8 @@ NP-VERSION-FILE /plugins/check_dbi /plugins/check_dig /plugins/check_disk +plugins/check_disk.d/.deps/ +plugins/check_disk.d/.dirstamp /plugins/check_dns /plugins/check_dummy /plugins/check_fping -- cgit v1.2.3-74-g34f1 From 59e0a258f9c0f393bf29cced1f35743f74e7b10c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:57:44 +0100 Subject: Migrate disk tests from lib, tool --- .gitignore | 3 +- configure.ac | 4 +- lib/tests/Makefile.am | 6 +- lib/tests/test_disk.c | 192 -------------------------------------- lib/tests/test_disk.t | 6 -- plugins/Makefile.am | 8 +- plugins/check_disk.d/utils_disk.c | 4 +- plugins/check_disk.d/utils_disk.h | 2 +- plugins/common.h | 6 +- plugins/tests/test_check_disk.c | 192 ++++++++++++++++++++++++++++++++++++++ plugins/tests/test_check_disk.t | 6 ++ 11 files changed, 216 insertions(+), 213 deletions(-) delete mode 100644 lib/tests/test_disk.c delete mode 100755 lib/tests/test_disk.t create mode 100644 plugins/tests/test_check_disk.c create mode 100755 plugins/tests/test_check_disk.t diff --git a/.gitignore b/.gitignore index 5245495e..8b14f429 100644 --- a/.gitignore +++ b/.gitignore @@ -114,7 +114,6 @@ NP-VERSION-FILE /lib/tests/Makefile.in /lib/tests/test_base64 /lib/tests/test_cmd -/lib/tests/test_disk /lib/tests/test_tcp /lib/tests/test_utils /lib/tests/utils_base.Po @@ -223,7 +222,7 @@ plugins/check_disk.d/.dirstamp /plugins/tests/Makefile /plugins/tests/Makefile.in /plugins/tests/test_utils -/plugins/tests/test_disk +/plugins/tests/test_check_disk /plugins/tests/test_check_swap /plugins/tests/.deps /plugins/tests/.dirstamp diff --git a/configure.ac b/configure.ac index 204fc6e3..fdc9b699 100644 --- a/configure.ac +++ b/configure.ac @@ -181,10 +181,10 @@ fi # Finally, define tests if we use libtap if test "$enable_libtap" = "yes" ; then - EXTRA_TEST="test_utils test_disk test_tcp test_cmd test_base64" + EXTRA_TEST="test_utils test_tcp test_cmd test_base64" AC_SUBST(EXTRA_TEST) - EXTRA_PLUGIN_TESTS="tests/test_check_swap" + EXTRA_PLUGIN_TESTS="tests/test_check_swap tests/test_check_disk" AC_SUBST(EXTRA_PLUGIN_TESTS) fi diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am index 9be94f6d..7798a72e 100644 --- a/lib/tests/Makefile.am +++ b/lib/tests/Makefile.am @@ -8,9 +8,9 @@ check_PROGRAMS = @EXTRA_TEST@ AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins -EXTRA_PROGRAMS = test_utils test_disk test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output +EXTRA_PROGRAMS = test_utils test_tcp test_cmd test_base64 test_ini1 test_ini3 test_opts1 test_opts2 test_opts3 test_generic_output -np_test_scripts = test_base64.t test_cmd.t test_disk.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t +np_test_scripts = test_base64.t test_cmd.t test_ini1.t test_ini3.t test_opts1.t test_opts2.t test_opts3.t test_tcp.t test_utils.t test_generic_output.t np_test_files = config-dos.ini config-opts.ini config-tiny.ini plugin.ini plugins.ini EXTRA_DIST = $(np_test_scripts) $(np_test_files) var @@ -29,7 +29,7 @@ AM_CFLAGS = -g -I$(top_srcdir)/lib -I$(top_srcdir)/gl $(tap_cflags) AM_LDFLAGS = $(tap_ldflags) -ltap LDADD = $(top_srcdir)/lib/libmonitoringplug.a $(top_srcdir)/gl/libgnu.a $(LIB_CRYPTO) -SOURCES = test_utils.c test_disk.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c +SOURCES = test_utils.c test_tcp.c test_cmd.c test_base64.c test_ini1.c test_ini3.c test_opts1.c test_opts2.c test_opts3.c test_generic_output.c test: ${noinst_PROGRAMS} perl -MTest::Harness -e '$$Test::Harness::switches=""; runtests(map {$$_ .= ".t"} @ARGV)' $(EXTRA_PROGRAMS) diff --git a/lib/tests/test_disk.c b/lib/tests/test_disk.c deleted file mode 100644 index c18db7a4..00000000 --- a/lib/tests/test_disk.c +++ /dev/null @@ -1,192 +0,0 @@ -/***************************************************************************** - * - * 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" -#include "utils_disk.h" -#include "tap.h" -#include "regex.h" - -void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc); - -int main(int argc, char **argv) { - struct name_list *exclude_filesystem = NULL; - struct name_list *exclude_fstype = NULL; - struct name_list *dummy_mountlist = NULL; - struct name_list *temp_name; - struct parameter_list *paths = NULL; - struct parameter_list *p, *prev = NULL, *last = NULL; - - struct mount_entry *dummy_mount_list; - struct mount_entry *me; - struct mount_entry **mtail = &dummy_mount_list; - int cflags = REG_NOSUB | REG_EXTENDED; - int found = 0, count = 0; - - plan_tests(33); - - ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); - np_add_name(&exclude_filesystem, "/var/log"); - ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now"); - ok(np_find_name(exclude_filesystem, "/home") == false, "/home not in list"); - np_add_name(&exclude_filesystem, "/home"); - ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now"); - ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list"); - - ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list"); - np_add_name(&exclude_fstype, "iso9660"); - ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now"); - - ok(np_find_name(exclude_filesystem, "iso9660") == false, "Make sure no clashing in variables"); - - /* - for (temp_name = exclude_filesystem; temp_name; temp_name = temp_name->next) { - printf("Name: %s\n", temp_name->name); - } - */ - - me = (struct mount_entry *)malloc(sizeof *me); - me->me_devname = strdup("/dev/c0t0d0s0"); - me->me_mountdir = strdup("/"); - *mtail = me; - mtail = &me->me_next; - - me = (struct mount_entry *)malloc(sizeof *me); - me->me_devname = strdup("/dev/c1t0d1s0"); - me->me_mountdir = strdup("/var"); - *mtail = me; - mtail = &me->me_next; - - me = (struct mount_entry *)malloc(sizeof *me); - me->me_devname = strdup("/dev/c2t0d0s0"); - me->me_mountdir = strdup("/home"); - *mtail = me; - mtail = &me->me_next; - - np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a")); - np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, strdup("regex on dev names:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, strdup("regex on non existent dev/path:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("/Foo"), cflags | REG_ICASE, 0, strdup("regi on non existent dev/path:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("/c.t0"), cflags, 3, strdup("partial devname regex match:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("c0t0"), cflags, 1, strdup("partial devname regex match:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("C0t0"), cflags | REG_ICASE, 1, strdup("partial devname regi match:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("home"), cflags, 1, strdup("partial pathname regex match:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("hOme"), cflags | REG_ICASE, 1, strdup("partial pathname regi match:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:")); - np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:")); - - np_add_parameter(&paths, "/home/groups"); - np_add_parameter(&paths, "/var"); - np_add_parameter(&paths, "/tmp"); - np_add_parameter(&paths, "/home/tonvoon"); - np_add_parameter(&paths, "/dev/c2t0d0s0"); - - np_set_best_match(paths, dummy_mount_list, false); - for (p = paths; p; p = p->name_next) { - struct mount_entry *temp_me; - temp_me = p->best_match; - if (!strcmp(p->name, "/home/groups")) { - ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/groups got right best match: /home"); - } else if (!strcmp(p->name, "/var")) { - ok(temp_me && !strcmp(temp_me->me_mountdir, "/var"), "/var got right best match: /var"); - } else if (!strcmp(p->name, "/tmp")) { - ok(temp_me && !strcmp(temp_me->me_mountdir, "/"), "/tmp got right best match: /"); - } else if (!strcmp(p->name, "/home/tonvoon")) { - ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/tonvoon got right best match: /home"); - } else if (!strcmp(p->name, "/dev/c2t0d0s0")) { - ok(temp_me && !strcmp(temp_me->me_devname, "/dev/c2t0d0s0"), "/dev/c2t0d0s0 got right best match: /dev/c2t0d0s0"); - } - } - - paths = NULL; /* Bad boy - should free, but this is a test suite */ - np_add_parameter(&paths, "/home/groups"); - np_add_parameter(&paths, "/var"); - np_add_parameter(&paths, "/tmp"); - np_add_parameter(&paths, "/home/tonvoon"); - np_add_parameter(&paths, "/home"); - - np_set_best_match(paths, dummy_mount_list, true); - for (p = paths; p; p = p->name_next) { - if (!strcmp(p->name, "/home/groups")) { - ok(!p->best_match, "/home/groups correctly not found"); - } else if (!strcmp(p->name, "/var")) { - ok(p->best_match, "/var found"); - } else if (!strcmp(p->name, "/tmp")) { - ok(!p->best_match, "/tmp correctly not found"); - } else if (!strcmp(p->name, "/home/tonvoon")) { - ok(!p->best_match, "/home/tonvoon not found"); - } else if (!strcmp(p->name, "/home")) { - ok(p->best_match, "/home found"); - } - } - - /* test deleting first element in paths */ - paths = np_del_parameter(paths, NULL); - for (p = paths; p; p = p->name_next) { - if (!strcmp(p->name, "/home/groups")) - found = 1; - } - ok(found == 0, "first element successfully deleted"); - found = 0; - - p = paths; - while (p) { - if (!strcmp(p->name, "/tmp")) - p = np_del_parameter(p, prev); - else { - prev = p; - p = p->name_next; - } - } - - for (p = paths; p; p = p->name_next) { - if (!strcmp(p->name, "/tmp")) - found = 1; - if (p->name_next) - prev = p; - else - last = p; - } - ok(found == 0, "/tmp element successfully deleted"); - - p = np_del_parameter(last, prev); - for (p = paths; p; p = p->name_next) { - if (!strcmp(p->name, "/home")) - found = 1; - last = p; - count++; - } - ok(found == 0, "last (/home) element successfully deleted"); - ok(count == 2, "two elements remaining"); - - return exit_status(); -} - -void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc) { - int matches = 0; - regex_t re; - struct mount_entry *me; - if (regcomp(&re, regstr, cflags) == 0) { - for (me = dummy_mount_list; me; me = me->me_next) { - if (np_regex_match_mount_entry(me, &re)) - matches++; - } - ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, matches); - - } else - ok(false, "regex '%s' not compilable", regstr); -} diff --git a/lib/tests/test_disk.t b/lib/tests/test_disk.t deleted file mode 100755 index da84dfdf..00000000 --- a/lib/tests/test_disk.t +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/perl -use Test::More; -if (! -e "./test_disk") { - plan skip_all => "./test_disk not compiled - please enable libtap library to test"; -} -exec "./test_disk"; diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 30283cb4..04fb7ed2 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -40,11 +40,13 @@ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ check_nagios check_by_ssh check_dns check_nt check_ide_smart \ check_procs check_mysql_query check_apt check_dbi check_curl \ \ - tests/test_check_swap + tests/test_check_swap \ + tests/test_check_disk SUBDIRS = picohttpparser -np_test_scripts = tests/test_check_swap.t +np_test_scripts = tests/test_check_swap.t \ + tests/test_check_disk.t EXTRA_DIST = t \ tests \ @@ -167,6 +169,8 @@ 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 +tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap +tests_test_check_disk_SOURCES = tests/test_check_disk.c ############################################################################## # secondary dependencies diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c index 1d806715..369c85d5 100644 --- a/plugins/check_disk.d/utils_disk.c +++ b/plugins/check_disk.d/utils_disk.c @@ -26,9 +26,9 @@ * *****************************************************************************/ -#include "common.h" +#include "../common.h" #include "utils_disk.h" -#include "gl/fsusage.h" +#include "../../gl/fsusage.h" #include void np_add_name(struct name_list **list, const char *name) { diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h index 1c68fed9..0c69f987 100644 --- a/plugins/check_disk.d/utils_disk.h +++ b/plugins/check_disk.d/utils_disk.h @@ -2,7 +2,7 @@ #include "../../config.h" #include "../../gl/mountlist.h" -#include "utils_base.h" +#include "../../lib/utils_base.h" #include "regex.h" #include diff --git a/plugins/common.h b/plugins/common.h index 603bae55..35d1e549 100644 --- a/plugins/common.h +++ b/plugins/common.h @@ -31,7 +31,7 @@ #ifndef _COMMON_H_ #define _COMMON_H_ -#include "config.h" +#include "../config.h" #include "../lib/monitoringplug.h" #ifdef HAVE_FEATURES_H @@ -110,7 +110,7 @@ /* GNU Libraries */ #include -#include "dirname.h" +#include "../gl/dirname.h" #include @@ -190,7 +190,7 @@ enum { * Internationalization * */ -#include "gettext.h" +#include "../gl/gettext.h" #define _(String) gettext (String) #if ! ENABLE_NLS # undef textdomain diff --git a/plugins/tests/test_check_disk.c b/plugins/tests/test_check_disk.c new file mode 100644 index 00000000..92d0d270 --- /dev/null +++ b/plugins/tests/test_check_disk.c @@ -0,0 +1,192 @@ +/***************************************************************************** + * + * 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" +#include "../check_disk.d/utils_disk.h" +#include "../../tap/tap.h" +#include "regex.h" + +void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc); + +int main(int argc, char **argv) { + struct name_list *exclude_filesystem = NULL; + struct name_list *exclude_fstype = NULL; + struct name_list *dummy_mountlist = NULL; + struct name_list *temp_name; + struct parameter_list *paths = NULL; + struct parameter_list *p, *prev = NULL, *last = NULL; + + struct mount_entry *dummy_mount_list; + struct mount_entry *me; + struct mount_entry **mtail = &dummy_mount_list; + int cflags = REG_NOSUB | REG_EXTENDED; + int found = 0, count = 0; + + plan_tests(33); + + ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); + np_add_name(&exclude_filesystem, "/var/log"); + ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now"); + ok(np_find_name(exclude_filesystem, "/home") == false, "/home not in list"); + np_add_name(&exclude_filesystem, "/home"); + ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now"); + ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list"); + + ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list"); + np_add_name(&exclude_fstype, "iso9660"); + ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now"); + + ok(np_find_name(exclude_filesystem, "iso9660") == false, "Make sure no clashing in variables"); + + /* + for (temp_name = exclude_filesystem; temp_name; temp_name = temp_name->next) { + printf("Name: %s\n", temp_name->name); + } + */ + + me = (struct mount_entry *)malloc(sizeof *me); + me->me_devname = strdup("/dev/c0t0d0s0"); + me->me_mountdir = strdup("/"); + *mtail = me; + mtail = &me->me_next; + + me = (struct mount_entry *)malloc(sizeof *me); + me->me_devname = strdup("/dev/c1t0d1s0"); + me->me_mountdir = strdup("/var"); + *mtail = me; + mtail = &me->me_next; + + me = (struct mount_entry *)malloc(sizeof *me); + me->me_devname = strdup("/dev/c2t0d0s0"); + me->me_mountdir = strdup("/home"); + *mtail = me; + mtail = &me->me_next; + + np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a")); + np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, strdup("regex on dev names:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, strdup("regex on non existent dev/path:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("/Foo"), cflags | REG_ICASE, 0, strdup("regi on non existent dev/path:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("/c.t0"), cflags, 3, strdup("partial devname regex match:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("c0t0"), cflags, 1, strdup("partial devname regex match:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("C0t0"), cflags | REG_ICASE, 1, strdup("partial devname regi match:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("home"), cflags, 1, strdup("partial pathname regex match:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("hOme"), cflags | REG_ICASE, 1, strdup("partial pathname regi match:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:")); + np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:")); + + np_add_parameter(&paths, "/home/groups"); + np_add_parameter(&paths, "/var"); + np_add_parameter(&paths, "/tmp"); + np_add_parameter(&paths, "/home/tonvoon"); + np_add_parameter(&paths, "/dev/c2t0d0s0"); + + np_set_best_match(paths, dummy_mount_list, false); + for (p = paths; p; p = p->name_next) { + struct mount_entry *temp_me; + temp_me = p->best_match; + if (!strcmp(p->name, "/home/groups")) { + ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/groups got right best match: /home"); + } else if (!strcmp(p->name, "/var")) { + ok(temp_me && !strcmp(temp_me->me_mountdir, "/var"), "/var got right best match: /var"); + } else if (!strcmp(p->name, "/tmp")) { + ok(temp_me && !strcmp(temp_me->me_mountdir, "/"), "/tmp got right best match: /"); + } else if (!strcmp(p->name, "/home/tonvoon")) { + ok(temp_me && !strcmp(temp_me->me_mountdir, "/home"), "/home/tonvoon got right best match: /home"); + } else if (!strcmp(p->name, "/dev/c2t0d0s0")) { + ok(temp_me && !strcmp(temp_me->me_devname, "/dev/c2t0d0s0"), "/dev/c2t0d0s0 got right best match: /dev/c2t0d0s0"); + } + } + + paths = NULL; /* Bad boy - should free, but this is a test suite */ + np_add_parameter(&paths, "/home/groups"); + np_add_parameter(&paths, "/var"); + np_add_parameter(&paths, "/tmp"); + np_add_parameter(&paths, "/home/tonvoon"); + np_add_parameter(&paths, "/home"); + + np_set_best_match(paths, dummy_mount_list, true); + for (p = paths; p; p = p->name_next) { + if (!strcmp(p->name, "/home/groups")) { + ok(!p->best_match, "/home/groups correctly not found"); + } else if (!strcmp(p->name, "/var")) { + ok(p->best_match, "/var found"); + } else if (!strcmp(p->name, "/tmp")) { + ok(!p->best_match, "/tmp correctly not found"); + } else if (!strcmp(p->name, "/home/tonvoon")) { + ok(!p->best_match, "/home/tonvoon not found"); + } else if (!strcmp(p->name, "/home")) { + ok(p->best_match, "/home found"); + } + } + + /* test deleting first element in paths */ + paths = np_del_parameter(paths, NULL); + for (p = paths; p; p = p->name_next) { + if (!strcmp(p->name, "/home/groups")) + found = 1; + } + ok(found == 0, "first element successfully deleted"); + found = 0; + + p = paths; + while (p) { + if (!strcmp(p->name, "/tmp")) + p = np_del_parameter(p, prev); + else { + prev = p; + p = p->name_next; + } + } + + for (p = paths; p; p = p->name_next) { + if (!strcmp(p->name, "/tmp")) + found = 1; + if (p->name_next) + prev = p; + else + last = p; + } + ok(found == 0, "/tmp element successfully deleted"); + + p = np_del_parameter(last, prev); + for (p = paths; p; p = p->name_next) { + if (!strcmp(p->name, "/home")) + found = 1; + last = p; + count++; + } + ok(found == 0, "last (/home) element successfully deleted"); + ok(count == 2, "two elements remaining"); + + return exit_status(); +} + +void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc) { + int matches = 0; + regex_t re; + struct mount_entry *me; + if (regcomp(&re, regstr, cflags) == 0) { + for (me = dummy_mount_list; me; me = me->me_next) { + if (np_regex_match_mount_entry(me, &re)) + matches++; + } + ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, matches); + + } else + ok(false, "regex '%s' not compilable", regstr); +} diff --git a/plugins/tests/test_check_disk.t b/plugins/tests/test_check_disk.t new file mode 100755 index 00000000..56354650 --- /dev/null +++ b/plugins/tests/test_check_disk.t @@ -0,0 +1,6 @@ +#!/usr/bin/perl +use Test::More; +if (! -e "./test_check_disk") { + plan skip_all => "./test_check_disk not compiled - please enable libtap library to test"; +} +exec "./test_check_disk"; -- cgit v1.2.3-74-g34f1 From 75cf0d307285c413d5547662e5ba677e1f70de62 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:23:06 +0100 Subject: Remove some unused code --- plugins/check_disk.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 2df89aa3..15ec06cd 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -65,14 +65,6 @@ const char *email = "devel@monitoring-plugins.org"; # define ERROR -1 #endif -/* For long options that have no equivalent short option, use a - non-character as a pseudo short option, starting with CHAR_MAX + 1. */ -enum { - SYNC_OPTION = CHAR_MAX + 1, - NO_SYNC_OPTION, - BLOCK_SIZE_OPTION -}; - #ifdef _AIX # pragma alloca #endif -- cgit v1.2.3-74-g34f1 From 42531fa92a97318f2b96265f501f37e7fd96ea4c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:23:33 +0100 Subject: Refactor test_check_disk.c --- plugins/tests/test_check_disk.c | 86 +++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/plugins/tests/test_check_disk.c b/plugins/tests/test_check_disk.c index 92d0d270..963a9413 100644 --- a/plugins/tests/test_check_disk.c +++ b/plugins/tests/test_check_disk.c @@ -24,21 +24,9 @@ void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc); int main(int argc, char **argv) { - struct name_list *exclude_filesystem = NULL; - struct name_list *exclude_fstype = NULL; - struct name_list *dummy_mountlist = NULL; - struct name_list *temp_name; - struct parameter_list *paths = NULL; - struct parameter_list *p, *prev = NULL, *last = NULL; - - struct mount_entry *dummy_mount_list; - struct mount_entry *me; - struct mount_entry **mtail = &dummy_mount_list; - int cflags = REG_NOSUB | REG_EXTENDED; - int found = 0, count = 0; - plan_tests(33); + struct name_list *exclude_filesystem = NULL; ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); np_add_name(&exclude_filesystem, "/var/log"); ok(np_find_name(exclude_filesystem, "/var/log") == true, "is in list now"); @@ -47,6 +35,7 @@ int main(int argc, char **argv) { ok(np_find_name(exclude_filesystem, "/home") == true, "is in list now"); ok(np_find_name(exclude_filesystem, "/var/log") == true, "/var/log still in list"); + struct name_list *exclude_fstype = NULL; ok(np_find_name(exclude_fstype, "iso9660") == false, "iso9660 not in list"); np_add_name(&exclude_fstype, "iso9660"); ok(np_find_name(exclude_fstype, "iso9660") == true, "is in list now"); @@ -59,7 +48,9 @@ int main(int argc, char **argv) { } */ - me = (struct mount_entry *)malloc(sizeof *me); + struct mount_entry *dummy_mount_list; + struct mount_entry **mtail = &dummy_mount_list; + struct mount_entry *me = (struct mount_entry *)malloc(sizeof *me); me->me_devname = strdup("/dev/c0t0d0s0"); me->me_mountdir = strdup("/"); *mtail = me; @@ -77,6 +68,7 @@ int main(int argc, char **argv) { *mtail = me; mtail = &me->me_next; + int cflags = REG_NOSUB | REG_EXTENDED; np_test_mount_entry_regex(dummy_mount_list, strdup("/"), cflags, 3, strdup("a")); np_test_mount_entry_regex(dummy_mount_list, strdup("/dev"), cflags, 3, strdup("regex on dev names:")); np_test_mount_entry_regex(dummy_mount_list, strdup("/foo"), cflags, 0, strdup("regex on non existent dev/path:")); @@ -89,6 +81,7 @@ int main(int argc, char **argv) { np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:")); np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:")); + struct parameter_list *paths = NULL; np_add_parameter(&paths, "/home/groups"); np_add_parameter(&paths, "/var"); np_add_parameter(&paths, "/tmp"); @@ -96,7 +89,7 @@ int main(int argc, char **argv) { np_add_parameter(&paths, "/dev/c2t0d0s0"); np_set_best_match(paths, dummy_mount_list, false); - for (p = paths; p; p = p->name_next) { + for (struct parameter_list *p = paths; p; p = p->name_next) { struct mount_entry *temp_me; temp_me = p->best_match; if (!strcmp(p->name, "/home/groups")) { @@ -120,7 +113,7 @@ int main(int argc, char **argv) { np_add_parameter(&paths, "/home"); np_set_best_match(paths, dummy_mount_list, true); - for (p = paths; p; p = p->name_next) { + for (struct parameter_list *p = paths; p; p = p->name_next) { if (!strcmp(p->name, "/home/groups")) { ok(!p->best_match, "/home/groups correctly not found"); } else if (!strcmp(p->name, "/var")) { @@ -134,59 +127,68 @@ int main(int argc, char **argv) { } } + bool found = false; /* test deleting first element in paths */ paths = np_del_parameter(paths, NULL); - for (p = paths; p; p = p->name_next) { - if (!strcmp(p->name, "/home/groups")) - found = 1; + for (struct parameter_list *p = paths; p; p = p->name_next) { + if (!strcmp(p->name, "/home/groups")) { + found = true; + } } - ok(found == 0, "first element successfully deleted"); - found = 0; + ok(!found, "first element successfully deleted"); + found = false; - p = paths; + struct parameter_list *prev = NULL; + struct parameter_list *p = paths; while (p) { - if (!strcmp(p->name, "/tmp")) + if (!strcmp(p->name, "/tmp")) { p = np_del_parameter(p, prev); - else { + } else { prev = p; p = p->name_next; } } - for (p = paths; p; p = p->name_next) { - if (!strcmp(p->name, "/tmp")) - found = 1; - if (p->name_next) - prev = p; - else - last = p; + struct parameter_list *last = NULL; + for (struct parameter_list *path = paths; path; path = path->name_next) { + if (!strcmp(path->name, "/tmp")) { + found = true; + } + if (path->name_next) { + prev = path; + } else { + last = path; + } } - ok(found == 0, "/tmp element successfully deleted"); + ok(!found, "/tmp element successfully deleted"); + int count = 0; p = np_del_parameter(last, prev); for (p = paths; p; p = p->name_next) { - if (!strcmp(p->name, "/home")) - found = 1; + if (!strcmp(p->name, "/home")) { + found = true; + } last = p; count++; } - ok(found == 0, "last (/home) element successfully deleted"); + ok(!found, "last (/home) element successfully deleted"); ok(count == 2, "two elements remaining"); return exit_status(); } void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc) { - int matches = 0; - regex_t re; - struct mount_entry *me; - if (regcomp(&re, regstr, cflags) == 0) { - for (me = dummy_mount_list; me; me = me->me_next) { - if (np_regex_match_mount_entry(me, &re)) + regex_t regex; + if (regcomp(®ex, regstr, cflags) == 0) { + int matches = 0; + for (struct mount_entry *me = dummy_mount_list; me; me = me->me_next) { + if (np_regex_match_mount_entry(me, ®ex)) { matches++; + } } ok(matches == expect, "%s '%s' matched %i/3 entries. ok: %i/3", desc, regstr, expect, matches); - } else + } else { ok(false, "regex '%s' not compilable", regstr); + } } -- cgit v1.2.3-74-g34f1 From a0710dbd72ea6314cf6a1ec50d2fa3c3c0543748 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:26:14 +0100 Subject: check_disk: Remove unnecessary NULL checks --- plugins/check_disk.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 15ec06cd..050298d6 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -878,34 +878,22 @@ void set_all_thresholds(struct parameter_list *path, char *warn_freespace_units, char *crit_freespace_percent, char *warn_usedspace_units, char *crit_usedspace_units, char *warn_usedspace_percent, char *crit_usedspace_percent, char *warn_usedinodes_percent, char *crit_usedinodes_percent, char *warn_freeinodes_percent, char *crit_freeinodes_percent) { - if (path->freespace_units != NULL) { - free(path->freespace_units); - } + free(path->freespace_units); set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); - if (path->freespace_percent != NULL) { - free(path->freespace_percent); - } + free(path->freespace_percent); set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); - if (path->usedspace_units != NULL) { - free(path->usedspace_units); - } + free(path->usedspace_units); set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); - if (path->usedspace_percent != NULL) { - free(path->usedspace_percent); - } + free(path->usedspace_percent); set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); - if (path->usedinodes_percent != NULL) { - free(path->usedinodes_percent); - } + free(path->usedinodes_percent); set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); - if (path->freeinodes_percent != NULL) { - free(path->freeinodes_percent); - } + free(path->freeinodes_percent); set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); } -- cgit v1.2.3-74-g34f1 From f84f614f219b395810e4ca35d5eb5f5a8d2f1d5b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:34:20 +0200 Subject: Bugfix in output --- lib/output.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/output.c b/lib/output.c index 61fbf832..01e50770 100644 --- a/lib/output.c +++ b/lib/output.c @@ -13,6 +13,7 @@ // == Global variables static mp_output_format output_format = MP_FORMAT_DEFAULT; +static mp_output_detail_level level_of_detail = MP_DETAIL_ALL; // == Prototypes == static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation); @@ -202,7 +203,12 @@ mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) { } mp_subcheck_list *scl = check.subchecks; - mp_state_enum result = check.default_state; + + if (scl == NULL) { + return check.default_state; + } + + mp_state_enum result = STATE_OK; while (scl != NULL) { result = max_state_alt(result, mp_compute_subcheck_state(scl->subcheck)); -- cgit v1.2.3-74-g34f1 From f413ac38e30e606beda4ef7f6bf1db40b49682d3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:34:42 +0200 Subject: Add selectable level of detail for output --- lib/output.c | 8 +++++++- lib/output.h | 16 +++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/output.c b/lib/output.c index 01e50770..c408a2f5 100644 --- a/lib/output.c +++ b/lib/output.c @@ -253,7 +253,9 @@ char *mp_fmt_output(mp_check check) { mp_subcheck_list *subchecks = check.subchecks; while (subchecks != NULL) { - asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); + if (level_of_detail == MP_DETAIL_ALL || mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) { + asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); + } subchecks = subchecks->next; } @@ -545,3 +547,7 @@ parsed_output_format mp_parse_output_format(char *format_string) { void mp_set_format(mp_output_format format) { output_format = format; } mp_output_format mp_get_format(void) { return output_format; } + +void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; } + +mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; } diff --git a/lib/output.h b/lib/output.h index 2bdfa074..3bd91f90 100644 --- a/lib/output.h +++ b/lib/output.h @@ -38,8 +38,18 @@ typedef enum output_format { /* * Format related functions */ - void mp_set_format(mp_output_format format); - mp_output_format mp_get_format(void); +void mp_set_format(mp_output_format format); +mp_output_format mp_get_format(void); + +// Output detail level + +typedef enum output_detail_level { + MP_DETAIL_ALL, + MP_DETAIL_NON_OK_ONLY, +} mp_output_detail_level; + +void mp_set_level_of_detail(mp_output_detail_level level); +mp_output_detail_level mp_get_level_of_detail(void); /* * The main state object of a plugin. Exists only ONCE per plugin. @@ -48,7 +58,7 @@ typedef enum output_format { * in the first layer of subchecks */ typedef struct { - char *summary; // Overall summary, if not set a summary will be automatically generated + char *summary; // Overall summary, if not set a summary will be automatically generated mp_subcheck_list *subchecks; } mp_check; -- cgit v1.2.3-74-g34f1 From 1921cfccd6d83edb15a04fdb143a3176fb96dd22 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:35:29 +0200 Subject: Always quote perfdata labels --- lib/perfdata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/perfdata.c b/lib/perfdata.c index 661756c5..4f9c9558 100644 --- a/lib/perfdata.c +++ b/lib/perfdata.c @@ -33,7 +33,7 @@ char *pd_value_to_string(const mp_perfdata_value pd) { char *pd_to_string(mp_perfdata pd) { assert(pd.label != NULL); char *result = NULL; - asprintf(&result, "%s=", pd.label); + asprintf(&result, "'%s'=", pd.label); asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); -- cgit v1.2.3-74-g34f1 From 6e108cc25e75ec74013838620b26dabdd480718a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:36:07 +0200 Subject: Add more helpers to perfdata functions --- lib/perfdata.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/perfdata.h | 12 ++++++++ 2 files changed, 99 insertions(+) diff --git a/lib/perfdata.c b/lib/perfdata.c index 4f9c9558..1742342e 100644 --- a/lib/perfdata.c +++ b/lib/perfdata.c @@ -514,3 +514,90 @@ perfdata_value_parser_wrapper parse_pd_value(const char *input) { } return result; } + +mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value) { + perfdata.max = value; + perfdata.max_present = true; + return perfdata; +} + +mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value) { + perfdata.min = value; + perfdata.min_present = true; + return perfdata; +} + +double mp_get_pd_value(mp_perfdata_value value) { + assert(value.type != PD_TYPE_NONE); + switch (value.type) { + case PD_TYPE_DOUBLE: + return value.pd_double; + case PD_TYPE_INT: + return (double)value.pd_int; + case PD_TYPE_UINT: + return (double)value.pd_uint; + default: + return 0; // just to make the compiler happy + } +} + +mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right) { + if (left.type == right.type) { + switch (left.type) { + case PD_TYPE_DOUBLE: + left.pd_double *= right.pd_double; + return left; + case PD_TYPE_INT: + left.pd_int *= right.pd_int; + return left; + case PD_TYPE_UINT: + left.pd_uint *= right.pd_uint; + return left; + default: + // what to here? + return left; + } + } + + // Different types, oh boy, just do the lazy thing for now and switch to double + switch (left.type) { + case PD_TYPE_INT: + left.pd_double = (double)left.pd_int; + left.type = PD_TYPE_DOUBLE; + break; + case PD_TYPE_UINT: + left.pd_double = (double)left.pd_uint; + left.type = PD_TYPE_DOUBLE; + break; + case PD_TYPE_DOUBLE: + default: + // already there + } + + switch (right.type) { + case PD_TYPE_INT: + right.pd_double = (double)right.pd_int; + right.type = PD_TYPE_DOUBLE; + break; + case PD_TYPE_UINT: + right.pd_double = (double)right.pd_uint; + right.type = PD_TYPE_DOUBLE; + break; + case PD_TYPE_DOUBLE: + default: + // already there + } + + left.pd_double *= right.pd_double; + return left; +} + +mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor) { + if (!range.end_infinity) { + range.end = mp_pd_value_multiply(range.end, factor); + } + if (!range.start_infinity) { + range.start = mp_pd_value_multiply(range.start, factor); + } + return range; +} diff --git a/lib/perfdata.h b/lib/perfdata.h index 74583ee5..cb552678 100644 --- a/lib/perfdata.h +++ b/lib/perfdata.h @@ -171,6 +171,11 @@ mp_perfdata_value mp_create_pd_value_u_long(unsigned long); mp_perfdata_value mp_create_pd_value_long_long(long long); mp_perfdata_value mp_create_pd_value_u_long_long(unsigned long long); +mp_perfdata mp_set_pd_max_value(mp_perfdata perfdata, mp_perfdata_value value); +mp_perfdata mp_set_pd_min_value(mp_perfdata perfdata, mp_perfdata_value value); + +double mp_get_pd_value(mp_perfdata_value value); + /* * Free the memory used by a pd_list */ @@ -178,6 +183,13 @@ void pd_list_free(pd_list[1]); int cmp_perfdata_value(mp_perfdata_value, mp_perfdata_value); +// ================ +// Helper functions +// ================ + +mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right); +mp_range mp_range_multiply(mp_range range, mp_perfdata_value factor); + // ================= // String formatters // ================= -- cgit v1.2.3-74-g34f1 From 0205694ce977af97a71c58e0ae3c431299ceb7bb Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:36:38 +0200 Subject: Fix wrong return state in threshold function --- lib/thresholds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/thresholds.c b/lib/thresholds.c index ddefae37..171f5093 100644 --- a/lib/thresholds.c +++ b/lib/thresholds.c @@ -51,7 +51,7 @@ mp_state_enum mp_get_pd_status(mp_perfdata perfdata) { } if (perfdata.warn_present) { if (mp_check_range(perfdata.value, perfdata.warn)) { - return STATE_CRITICAL; + return STATE_WARNING; } } -- cgit v1.2.3-74-g34f1 From 0bca1d1aa36b13723e77672eae162352e9be99c9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:36:55 +0200 Subject: Implement some helper functions for thresholds --- lib/thresholds.c | 12 ++++++++++++ lib/thresholds.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/lib/thresholds.c b/lib/thresholds.c index 171f5093..de2b9315 100644 --- a/lib/thresholds.c +++ b/lib/thresholds.c @@ -57,3 +57,15 @@ mp_state_enum mp_get_pd_status(mp_perfdata perfdata) { return STATE_OK; } + +mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn) { + thlds.warning = warn; + thlds.warning_is_set = true; + return thlds; +} + +mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit) { + thlds.critical = crit; + thlds.critical_is_set = true; + return thlds; +} diff --git a/lib/thresholds.h b/lib/thresholds.h index 4e7defee..5f9f9247 100644 --- a/lib/thresholds.h +++ b/lib/thresholds.h @@ -24,5 +24,8 @@ mp_perfdata mp_pd_set_thresholds(mp_perfdata /* pd */, mp_thresholds /* th */); mp_state_enum mp_get_pd_status(mp_perfdata /* pd */); +mp_thresholds mp_thresholds_set_warn(mp_thresholds thlds, mp_range warn); +mp_thresholds mp_thresholds_set_crit(mp_thresholds thlds, mp_range crit); + char *fmt_threshold_warning(thresholds th); char *fmt_threshold_critical(thresholds th); -- cgit v1.2.3-74-g34f1 From 908aed4e6f9072e601a189d4ceff3152bdecc49d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:37:48 +0200 Subject: Refactor check_disk and library functions --- plugins/check_disk.c | 1069 ++++++++++++++++++++----------------- plugins/check_disk.d/config.h | 92 ---- plugins/check_disk.d/utils_disk.c | 469 ++++++++++++---- plugins/check_disk.d/utils_disk.h | 142 ++++- 4 files changed, 1048 insertions(+), 724 deletions(-) delete mode 100644 plugins/check_disk.d/config.h diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 050298d6..3cab816d 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -33,6 +33,10 @@ const char *email = "devel@monitoring-plugins.org"; #include "states.h" #include "common.h" +#include "output.h" +#include "perfdata.h" +#include "utils_base.h" +#include "lib/thresholds.h" #ifdef HAVE_SYS_STAT_H # include @@ -48,10 +52,9 @@ const char *email = "devel@monitoring-plugins.org"; #include #include "./popen.h" #include "./utils.h" -#include "./check_disk.d/utils_disk.h" #include "../gl/fsusage.h" #include "../gl/mountlist.h" -#include "check_disk.d/config.h" +#include "./check_disk.d/utils_disk.h" #if HAVE_LIMITS_H # include @@ -74,20 +77,22 @@ typedef struct { check_disk_config config; } check_disk_config_wrapper; static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static void set_all_thresholds(struct parameter_list *path, char * /*warn_freespace_units*/, char * /*crit_freespace_units*/, - char * /*warn_freespace_percent*/, char * /*crit_freespace_percent*/, char * /*warn_usedspace_units*/, - char * /*crit_usedspace_units*/, char * /*warn_usedspace_percent*/, char * /*crit_usedspace_percent*/, - char * /*warn_usedinodes_percent*/, char * /*crit_usedinodes_percent*/, char * /*warn_freeinodes_percent*/, - char * /*crit_freeinodes_percent*/); -static void print_help(void); -void print_usage(void); + +static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units, + char *warn_freespace_percent, char *crit_freespace_percent, char *warn_freeinodes_percent, + char *crit_freeinodes_percent); static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/); -static bool stat_path(struct parameter_list * /*parameters*/, bool /*ignore_missing*/); -static void get_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp, bool /*ignore_missing*/, - bool /*freespace_ignore_reserved*/, uintmax_t /*mult*/, struct parameter_list * /*path_select_list*/, - struct name_list * /*seen*/); -static void get_path_stats(struct parameter_list * /*parameters*/, struct fs_usage *fsp, bool /*freespace_ignore_reserved*/, - uintmax_t /*mult*/, struct name_list * /*seen*/); +static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/); + +/* + * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control how reserved + * and inodes should be judged (ignored or not) + */ +static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp, bool freespace_ignore_reserved); +static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit); + +void print_usage(void); +static void print_help(void); static int verbose = 0; @@ -100,7 +105,7 @@ int main(int argc, char **argv) { char mountdir[32]; #endif - /* Parse extra opts if any */ + // Parse extra opts if any argv = np_extra_opts(&argc, argv, progname); check_disk_config_wrapper tmp_config = process_arguments(argc, argv); @@ -110,289 +115,222 @@ int main(int argc, char **argv) { check_disk_config config = tmp_config.config; - /* If a list of paths has not been selected, find entire - mount list and create list of paths - */ - if (!config.path_selected && !config.path_ignored) { - for (struct mount_entry *me = config.mount_list; me; me = me->me_next) { - struct parameter_list *path = NULL; - if (!(path = np_find_parameter(config.path_select_list, me->me_mountdir))) { - path = np_add_parameter(&config.path_select_list, me->me_mountdir); - } - path->best_match = me; - path->group = config.group; - set_all_thresholds(path, config.warn_freespace_units, config.crit_freespace_units, config.warn_freespace_percent, - config.crit_freespace_percent, config.warn_usedspace_units, config.crit_usedspace_units, - config.warn_usedspace_percent, config.crit_usedspace_percent, config.warn_usedinodes_percent, - config.crit_usedinodes_percent, config.warn_freeinodes_percent, config.crit_freeinodes_percent); - } + if (config.output_format_is_set) { + mp_set_format(config.output_format); } - if (!config.path_ignored) { - np_set_best_match(config.path_select_list, config.mount_list, config.exact_match); + if (config.erronly) { + mp_set_level_of_detail(MP_DETAIL_NON_OK_ONLY); } - /* Error if no match found for specified paths */ - struct parameter_list *temp_list = config.path_select_list; + if (!config.path_ignored) { + mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list, config.exact_match); + } - char *ignored = strdup(""); - while (config.path_select_list) { - if (!config.path_select_list->best_match && config.ignore_missing) { - /* If the first element will be deleted, the temp_list must be updated with the new start address as well */ - if (config.path_select_list == temp_list) { - temp_list = config.path_select_list->name_next; - } - /* Add path argument to list of ignored paths to inform about missing paths being ignored and not alerted */ - xasprintf(&ignored, "%s %s;", ignored, config.path_select_list->name); + // Error if no match found for specified paths + for (parameter_list_elem *elem = config.path_select_list.first; elem;) { + if (!elem->best_match && config.ignore_missing) { /* Delete the path from the list so that it is not stat-checked later in the code. */ - config.path_select_list = np_del_parameter(config.path_select_list, config.path_select_list->name_prev); - } else if (!config.path_select_list->best_match) { + elem = mp_int_fs_list_del(&config.path_select_list, elem); + continue; + } + if (!elem->best_match) { /* Without --ignore-missing option, exit with Critical state. */ - die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), config.path_select_list->name); - } else { - /* Continue jumping through the list */ - config.path_select_list = config.path_select_list->name_next; + die(STATE_CRITICAL, _("DISK %s: %s not found\n"), _("CRITICAL"), elem->name); } - } - config.path_select_list = temp_list; - - mp_state_enum result = STATE_UNKNOWN; - if (!config.path_select_list && config.ignore_missing) { - result = STATE_OK; - if (verbose >= 2) { - printf("None of the provided paths were found\n"); - } + elem = mp_int_fs_list_get_next(elem); } - mp_state_enum filesystem_result = STATE_UNKNOWN; - char *perf = strdup(""); - char *perf_ilabel = strdup(""); - char *output = strdup(""); - struct parameter_list *path = NULL; - /* Process for every path in list */ - for (path = config.path_select_list; path; path = path->name_next) { - if (verbose >= 3 && path->freespace_percent->warning != NULL && path->freespace_percent->critical != NULL) { - printf("Thresholds(pct) for %s warn: %f crit %f\n", path->name, path->freespace_percent->warning->end, - path->freespace_percent->critical->end); + mp_check overall = mp_check_init(); + if (config.path_select_list.length == 0) { + mp_subcheck none_sc = mp_subcheck_init(); + xasprintf(&none_sc.output, "No filesystems were found for the provided parameters"); + if (config.ignore_missing) { + none_sc = mp_set_subcheck_state(none_sc, STATE_OK); + } else { + none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN); + if (verbose >= 2) { + printf("None of the provided paths were found\n"); + } } + mp_add_subcheck_to_check(&overall, none_sc); + mp_exit(overall); + } - if (verbose >= 3 && path->group != NULL) { - printf("Group of %s: %s\n", path->name, path->group); + // Filter list first + for (parameter_list_elem *path = config.path_select_list.first; path;) { + if (!path->best_match) { + path = mp_int_fs_list_del(&config.path_select_list, path); + continue; } - // reset filesystem result - filesystem_result = STATE_UNKNOWN; - struct mount_entry *mount_entry = path->best_match; - if (!mount_entry) { - continue; - } - #ifdef __CYGWIN__ if (strncmp(path->name, "/cygdrive/", 10) != 0 || strlen(path->name) > 11) { + path = mp_int_fs_list_del(&config.path_select_list, path); continue; } + + char *mountdir = NULL; snprintf(mountdir, sizeof(mountdir), "%s:\\", me->me_mountdir + 10); if (GetDriveType(mountdir) != DRIVE_FIXED) { - me->me_remote = 1; + mount_entry->me_remote = 1; } #endif - /* Filters */ /* Remove filesystems already seen */ if (np_seen_name(config.seen, mount_entry->me_mountdir)) { + path = mp_int_fs_list_del(&config.path_select_list, path); continue; } - np_add_name(&config.seen, mount_entry->me_mountdir); if (path->group == NULL) { - /* Skip remote filesystems if we're not interested in them */ - if (mount_entry->me_remote && config.show_local_fs) { - if (config.stat_remote_fs) { - if (!stat_path(path, config.ignore_missing) && config.ignore_missing) { - result = STATE_OK; - xasprintf(&ignored, "%s %s;", ignored, path->name); - } - } - continue; - /* Skip pseudo fs's if we haven't asked for all fs's */ - } if (config.fs_exclude_list && np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) { + // Skip excluded fs's + path = mp_int_fs_list_del(&config.path_select_list, path); continue; - /* Skip excluded fs's */ } + if (config.device_path_exclude_list && (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) || np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) { + // Skip excluded device or mount paths + path = mp_int_fs_list_del(&config.path_select_list, path); continue; - /* Skip not included fstypes */ } + if (config.fs_include_list && !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) { + // Skip not included fstypes + path = mp_int_fs_list_del(&config.path_select_list, path); continue; } - } - - if (!stat_path(path, config.ignore_missing)) { - if (config.ignore_missing) { - result = STATE_OK; - xasprintf(&ignored, "%s %s;", ignored, path->name); - } - continue; - } - struct fs_usage fsp = {0}; - get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp); - - if (fsp.fsu_blocks && strcmp("none", mount_entry->me_mountdir)) { - get_stats(path, &fsp, config.ignore_missing, config.freespace_ignore_reserved, config.mult, config.path_select_list, - config.seen); - - if (verbose >= 3) { - printf("For %s, used_pct=%f free_pct=%f used_units=%lu free_units=%lu total_units=%lu used_inodes_pct=%f " - "free_inodes_pct=%f fsp.fsu_blocksize=%lu mult=%lu\n", - mount_entry->me_mountdir, path->dused_pct, path->dfree_pct, path->dused_units, path->dfree_units, path->dtotal_units, - path->dused_inodes_percent, path->dfree_inodes_percent, fsp.fsu_blocksize, config.mult); - } - - /* Threshold comparisons */ - - mp_state_enum temp_result = get_status(path->dfree_units, path->freespace_units); - if (verbose >= 3) { - printf("Freespace_units result=%d\n", temp_result); + /* Skip remote filesystems if we're not interested in them */ + if (mount_entry->me_remote && config.show_local_fs) { + if (config.stat_remote_fs) { + // TODO Stat here + if (!stat_path(path, config.ignore_missing) && config.ignore_missing) { + } + } + continue; } - filesystem_result = max_state(filesystem_result, temp_result); - temp_result = get_status(path->dfree_pct, path->freespace_percent); - if (verbose >= 3) { - printf("Freespace%% result=%d\n", temp_result); + // TODO why stat here? remove unstatable fs? + if (!stat_path(path, config.ignore_missing)) { + // if (config.ignore_missing) { + // xasprintf(&ignored, "%s %s;", ignored, path->name); + // } + // not accessible, remove from list + path = mp_int_fs_list_del(&config.path_select_list, path); + continue; } - filesystem_result = max_state(filesystem_result, temp_result); + } - temp_result = get_status(path->dused_units, path->usedspace_units); - if (verbose >= 3) { - printf("Usedspace_units result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); + path = mp_int_fs_list_get_next(path); + } - temp_result = get_status(path->dused_pct, path->usedspace_percent); - if (verbose >= 3) { - printf("Usedspace_percent result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); + // now get the actual measurements + for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem;) { + // Get actual metrics here + struct mount_entry *mount_entry = filesystem->best_match; + struct fs_usage fsp = {0}; + get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp); - temp_result = get_status(path->dused_inodes_percent, path->usedinodes_percent); - if (verbose >= 3) { - printf("Usedinodes_percent result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); + if (fsp.fsu_blocks != 0 && strcmp("none", mount_entry->me_mountdir) != 0) { + *filesystem = get_path_stats(*filesystem, fsp, config.freespace_ignore_reserved); - temp_result = get_status(path->dfree_inodes_percent, path->freeinodes_percent); if (verbose >= 3) { - printf("Freeinodes_percent result=%d\n", temp_result); - } - filesystem_result = max_state(filesystem_result, temp_result); - - result = max_state(result, filesystem_result); - - /* What a mess of units. The output shows free space, the perf data shows used space. Yikes! - Hack here. Trying to get warn/crit levels from freespace_(units|percent) for perf - data. Assumption that start=0. Roll on new syntax... - */ - - /* *_high_tide must be reinitialized at each run */ - uint64_t warning_high_tide = UINT64_MAX; - - if (path->freespace_units->warning != NULL) { - warning_high_tide = (path->dtotal_units - path->freespace_units->warning->end) * config.mult; - } - if (path->freespace_percent->warning != NULL) { - warning_high_tide = min(warning_high_tide, (uint64_t)((1.0 - path->freespace_percent->warning->end / 100) * - (path->dtotal_units * config.mult))); - } - - uint64_t critical_high_tide = UINT64_MAX; - - if (path->freespace_units->critical != NULL) { - critical_high_tide = (path->dtotal_units - path->freespace_units->critical->end) * config.mult; - } - if (path->freespace_percent->critical != NULL) { - critical_high_tide = min(critical_high_tide, (uint64_t)((1.0 - path->freespace_percent->critical->end / 100) * - (path->dtotal_units * config.mult))); - } - - /* Nb: *_high_tide are unset when == UINT64_MAX */ - xasprintf(&perf, "%s %s", perf, - perfdata_uint64((!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname - : mount_entry->me_mountdir, - path->dused_units * config.mult, "B", (warning_high_tide != UINT64_MAX), warning_high_tide, - (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, - path->dtotal_units * config.mult)); - - if (config.display_inodes_perfdata) { - /* *_high_tide must be reinitialized at each run */ - warning_high_tide = UINT64_MAX; - critical_high_tide = UINT64_MAX; - - if (path->freeinodes_percent->warning != NULL) { - warning_high_tide = (uint64_t)fabs( - min((double)warning_high_tide, (double)(1.0 - path->freeinodes_percent->warning->end / 100) * path->inodes_total)); - } - if (path->freeinodes_percent->critical != NULL) { - critical_high_tide = (uint64_t)fabs(min( - (double)critical_high_tide, (double)(1.0 - path->freeinodes_percent->critical->end / 100) * path->inodes_total)); - } - - xasprintf(&perf_ilabel, "%s (inodes)", - (!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname - : mount_entry->me_mountdir); - /* Nb: *_high_tide are unset when == UINT64_MAX */ - xasprintf(&perf, "%s %s", perf, - perfdata_uint64(perf_ilabel, path->inodes_used, "", (warning_high_tide != UINT64_MAX), warning_high_tide, - (critical_high_tide != UINT64_MAX), critical_high_tide, true, 0, true, path->inodes_total)); + printf("For %s, used_units=%lu free_units=%lu total_units=%lu " + "fsp.fsu_blocksize=%lu\n", + mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes, filesystem->total_bytes, + fsp.fsu_blocksize); } + } else { + // failed to retrieve file system data or not mounted? + filesystem = mp_int_fs_list_del(&config.path_select_list, filesystem); + continue; + } + filesystem = mp_int_fs_list_get_next(filesystem); + } - if (filesystem_result == STATE_OK && config.erronly && !verbose) { - continue; + if (verbose > 2) { + for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; + filesystem = mp_int_fs_list_get_next(filesystem)) { + assert(filesystem->best_match != NULL); + if (filesystem->best_match == NULL) { + printf("Filesystem path %s has no mount_entry!\n", filesystem->name); + } else { + // printf("Filesystem path %s has a mount_entry!\n", filesystem->name); } + } + } - char *flag_header = NULL; - if (filesystem_result && verbose >= 1) { - xasprintf(&flag_header, " %s [", state_text(filesystem_result)); + measurement_unit_list *measurements = NULL; + measurement_unit_list *current = NULL; + // create measuring units, because of groups + for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; filesystem = mp_int_fs_list_get_next(filesystem)) { + assert(filesystem->best_match != NULL); + + if (filesystem->group == NULL) { + // create a measurement unit for the fs + measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + if (measurements == NULL) { + measurements = current = add_measurement_list(NULL, unit); } else { - xasprintf(&flag_header, ""); + current = add_measurement_list(measurements, unit); } - xasprintf(&output, "%s%s %s %llu%s (%.1f%%", output, flag_header, - (!strcmp(mount_entry->me_mountdir, "none") || config.display_mntp) ? mount_entry->me_devname - : mount_entry->me_mountdir, - path->dfree_units, config.units, path->dfree_pct); - if (path->dused_inodes_percent < 0) { - xasprintf(&output, "%s inode=-)%s;", output, (filesystem_result ? "]" : "")); + } else { + // Grouped elements are consecutive + if (measurements == NULL) { + // first entry + measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + unit.name = strdup(filesystem->group); + measurements = current = add_measurement_list(NULL, unit); } else { - xasprintf(&output, "%s inode=%.0f%%)%s;", output, path->dfree_inodes_percent, - ((filesystem_result && verbose >= 1) ? "]" : "")); + // if this is the first element of a group, the name of the previos entry is different + if (strcmp(filesystem->group, current->unit.name) != 0) { + // so, this must be the first element of a group + measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + unit.name = filesystem->group; + current = add_measurement_list(measurements, unit); + + } else { + // NOT the first entry of a group, add info to the other one + current->unit = add_filesystem_to_measurement_unit(current->unit, *filesystem); + } } - free(flag_header); } } - char *preamble = " - free space:"; - if (strcmp(output, "") == 0 && !config.erronly) { - preamble = ""; - xasprintf(&output, " - No disks were found for provided parameters"); + /* Process for every path in list */ + if (measurements != NULL) { + for (measurement_unit_list *unit = measurements; unit; unit = unit->next) { + mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata, config.display_unit); + mp_add_subcheck_to_check(&overall, unit_sc); + } + } else { + // Aparently no machting fs found + mp_subcheck none_sc = mp_subcheck_init(); + xasprintf(&none_sc.output, "No filesystems were found for the provided parameters"); + + if (config.ignore_missing) { + none_sc = mp_set_subcheck_state(none_sc, STATE_OK); + } else { + none_sc = mp_set_subcheck_state(none_sc, STATE_UNKNOWN); + } + mp_add_subcheck_to_check(&overall, none_sc); } - char *ignored_preamble = " - ignored paths:"; - printf("DISK %s%s%s%s%s|%s\n", state_text(result), (config.erronly && (result == STATE_OK)) ? "" : preamble, output, - (strcmp(ignored, "") == 0) ? "" : ignored_preamble, ignored, perf); - return result; + mp_exit(overall); } double calculate_percent(uintmax_t value, uintmax_t total) { double pct = -1; if (value <= DBL_MAX && total != 0) { - pct = (double)value / total * 100.0; + pct = (double)value / (double)total * 100.0; } + return pct; } @@ -409,11 +347,15 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { return result; } + enum { + output_format_index = CHAR_MAX + 1, + display_unit_index, + }; + static struct option longopts[] = {{"timeout", required_argument, 0, 't'}, {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"iwarning", required_argument, 0, 'W'}, - /* Dang, -C is taken. We might want to reshuffle this. */ {"icritical", required_argument, 0, 'K'}, {"kilobytes", no_argument, 0, 'k'}, {"megabytes", no_argument, 0, 'm'}, @@ -446,6 +388,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { {"clear", no_argument, 0, 'C'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"output-format", required_argument, 0, output_format_index}, + {"display-unit", required_argument, 0, display_unit_index}, {0, 0, 0, 0}}; for (int index = 1; index < argc; index++) { @@ -456,6 +400,17 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { int cflags = REG_NOSUB | REG_EXTENDED; int default_cflags = cflags; + char *warn_freespace_units = NULL; + char *crit_freespace_units = NULL; + char *warn_freespace_percent = NULL; + char *crit_freespace_percent = NULL; + char *warn_freeinodes_percent = NULL; + char *crit_freeinodes_percent = NULL; + + bool path_selected = false; + char *group = NULL; + byte_unit unit = MebiBytes; + result.config.mount_list = read_file_system_list(false); np_add_regex(&result.config.fs_exclude_list, "iso9660", REG_EXTENDED); @@ -485,24 +440,24 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (strstr(optarg, "%")) { if (*optarg == '@') { - result.config.warn_freespace_percent = optarg; + warn_freespace_percent = optarg; } else { - xasprintf(&result.config.warn_freespace_percent, "@%s", optarg); + xasprintf(&warn_freespace_percent, "@%s", optarg); } } else { if (*optarg == '@') { - result.config.warn_freespace_units = optarg; + warn_freespace_units = optarg; } else { - xasprintf(&result.config.warn_freespace_units, "@%s", optarg); + xasprintf(&warn_freespace_units, "@%s", optarg); } } break; /* Awful mistake where the range values do not make sense. Normally, - you alert if the value is within the range, but since we are using - freespace, we have to alert if outside the range. Thus we artificially - force @ at the beginning of the range, so that it is backwards compatible - */ + * you alert if the value is within the range, but since we are using + * freespace, we have to alert if outside the range. Thus we artificially + * force @ at the beginning of the range, so that it is backwards compatible + */ case 'c': /* critical threshold */ if (!is_percentage_expression(optarg) && !is_numeric(optarg)) { die(STATE_UNKNOWN, "Argument for --critical invalid or missing: %s\n", optarg); @@ -510,84 +465,92 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (strstr(optarg, "%")) { if (*optarg == '@') { - result.config.crit_freespace_percent = optarg; + crit_freespace_percent = optarg; } else { - xasprintf(&result.config.crit_freespace_percent, "@%s", optarg); + xasprintf(&crit_freespace_percent, "@%s", optarg); } } else { if (*optarg == '@') { - result.config.crit_freespace_units = optarg; + crit_freespace_units = optarg; } else { - xasprintf(&result.config.crit_freespace_units, "@%s", optarg); + xasprintf(&crit_freespace_units, "@%s", optarg); } } break; case 'W': /* warning inode threshold */ if (*optarg == '@') { - result.config.warn_freeinodes_percent = optarg; + warn_freeinodes_percent = optarg; } else { - xasprintf(&result.config.warn_freeinodes_percent, "@%s", optarg); + xasprintf(&warn_freeinodes_percent, "@%s", optarg); } break; case 'K': /* critical inode threshold */ if (*optarg == '@') { - result.config.crit_freeinodes_percent = optarg; + crit_freeinodes_percent = optarg; } else { - xasprintf(&result.config.crit_freeinodes_percent, "@%s", optarg); + xasprintf(&crit_freeinodes_percent, "@%s", optarg); } break; case 'u': - free(result.config.units); if (!strcasecmp(optarg, "bytes")) { - result.config.mult = (uintmax_t)1; - result.config.units = strdup("B"); + unit = Bytes; } else if (!strcmp(optarg, "KiB")) { - result.config.mult = (uintmax_t)1024; - result.config.units = strdup("KiB"); + unit = KibiBytes; } else if (!strcmp(optarg, "kB")) { - result.config.mult = (uintmax_t)1000; - result.config.units = strdup("kB"); + unit = KiloBytes; } else if (!strcmp(optarg, "MiB")) { - result.config.mult = (uintmax_t)1024 * 1024; - result.config.units = strdup("MiB"); + unit = MebiBytes; } else if (!strcmp(optarg, "MB")) { - result.config.mult = (uintmax_t)1000 * 1000; - result.config.units = strdup("MB"); + unit = MegaBytes; } else if (!strcmp(optarg, "GiB")) { - result.config.mult = (uintmax_t)1024 * 1024 * 1024; - result.config.units = strdup("GiB"); + unit = GibiBytes; } else if (!strcmp(optarg, "GB")) { - result.config.mult = (uintmax_t)1000 * 1000 * 1000; - result.config.units = strdup("GB"); + unit = GigaBytes; } else if (!strcmp(optarg, "TiB")) { - result.config.mult = (uintmax_t)1024 * 1024 * 1024 * 1024; - result.config.units = strdup("TiB"); + unit = TebiBytes; } else if (!strcmp(optarg, "TB")) { - result.config.mult = (uintmax_t)1000 * 1000 * 1000 * 1000; - result.config.units = strdup("TB"); + unit = TeraBytes; } else if (!strcmp(optarg, "PiB")) { - result.config.mult = (uintmax_t)1024 * 1024 * 1024 * 1024 * 1024; - result.config.units = strdup("PiB"); + unit = PebiBytes; } else if (!strcmp(optarg, "PB")) { - result.config.mult = (uintmax_t)1000 * 1000 * 1000 * 1000 * 1000; - result.config.units = strdup("PB"); + unit = PetaBytes; } else { die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); } - if (result.config.units == NULL) { - die(STATE_UNKNOWN, _("failed allocating storage for '%s'\n"), "units"); - } break; - case 'k': /* display mountpoint */ - result.config.mult = 1024; - free(result.config.units); - result.config.units = strdup("kiB"); + case 'k': + unit = KibiBytes; break; - case 'm': /* display mountpoint */ - result.config.mult = 1024 * 1024; - free(result.config.units); - result.config.units = strdup("MiB"); + case 'm': + unit = MebiBytes; + break; + case display_unit_index: + if (!strcasecmp(optarg, "bytes")) { + result.config.display_unit = Bytes; + } else if (!strcmp(optarg, "KiB")) { + result.config.display_unit = KibiBytes; + } else if (!strcmp(optarg, "kB")) { + result.config.display_unit = KiloBytes; + } else if (!strcmp(optarg, "MiB")) { + result.config.display_unit = MebiBytes; + } else if (!strcmp(optarg, "MB")) { + result.config.display_unit = MegaBytes; + } else if (!strcmp(optarg, "GiB")) { + result.config.display_unit = GibiBytes; + } else if (!strcmp(optarg, "GB")) { + result.config.display_unit = GigaBytes; + } else if (!strcmp(optarg, "TiB")) { + result.config.display_unit = TebiBytes; + } else if (!strcmp(optarg, "TB")) { + result.config.display_unit = TeraBytes; + } else if (!strcmp(optarg, "PiB")) { + result.config.display_unit = PebiBytes; + } else if (!strcmp(optarg, "PB")) { + result.config.display_unit = PetaBytes; + } else { + die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); + } break; case 'L': result.config.stat_remote_fs = true; @@ -599,43 +562,34 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.display_inodes_perfdata = true; break; case 'p': /* select path */ { - if (!(result.config.warn_freespace_units || result.config.crit_freespace_units || result.config.warn_freespace_percent || - result.config.crit_freespace_percent || result.config.warn_usedspace_units || result.config.crit_usedspace_units || - result.config.warn_usedspace_percent || result.config.crit_usedspace_percent || result.config.warn_usedinodes_percent || - result.config.crit_usedinodes_percent || result.config.warn_freeinodes_percent || - result.config.crit_freeinodes_percent)) { + if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || + warn_freeinodes_percent || crit_freeinodes_percent)) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); } /* add parameter if not found. overwrite thresholds if path has already been added */ - struct parameter_list *se; - if (!(se = np_find_parameter(result.config.path_select_list, optarg))) { - se = np_add_parameter(&result.config.path_select_list, optarg); - - struct stat stat_buf = {}; - if (stat(optarg, &stat_buf) && result.config.ignore_missing) { - result.config.path_ignored = true; - break; - } + parameter_list_elem *search_entry; + if (!(search_entry = mp_int_fs_list_find(result.config.path_select_list, optarg))) { + search_entry = mp_int_fs_list_append(&result.config.path_select_list, optarg); + + // struct stat stat_buf = {}; + // if (stat(optarg, &stat_buf) && result.config.ignore_missing) { + // result.config.path_ignored = true; + // break; + // } } - se->group = result.config.group; - set_all_thresholds( - se, result.config.warn_freespace_units, result.config.crit_freespace_units, result.config.warn_freespace_percent, - result.config.crit_freespace_percent, result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, result.config.warn_usedinodes_percent, - result.config.crit_usedinodes_percent, result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + search_entry->group = group; + set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + + warn_freeinodes_percent, crit_freeinodes_percent); /* With autofs, it is required to stat() the path before re-populating the mount_list */ - if (!stat_path(se, result.config.ignore_missing)) { - break; - } - /* NB: We can't free the old mount_list "just like that": both list pointers and struct - * pointers are copied around. One of the reason it wasn't done yet is that other parts - * of check_disk need the same kind of cleanup so it'd better be done as a whole */ - result.config.mount_list = read_file_system_list(false); - np_set_best_match(se, result.config.mount_list, result.config.exact_match); + // if (!stat_path(se, result.config.ignore_missing)) { + // break; + // } + mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); - result.config.path_selected = true; + path_selected = true; } break; case 'x': /* exclude path or partition */ np_add_name(&result.config.device_path_exclude_list, optarg); @@ -667,7 +621,7 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.erronly = true; break; case 'E': - if (result.config.path_selected) { + if (path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); } result.config.exact_match = true; @@ -676,16 +630,16 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.freespace_ignore_reserved = true; break; case 'g': - if (result.config.path_selected) { + if (path_selected) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); } - result.config.group = optarg; + group = optarg; break; case 'I': cflags |= REG_ICASE; // Intentional fallthrough case 'i': { - if (!result.config.path_selected) { + if (!path_selected) { die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); } @@ -697,29 +651,20 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); } - struct parameter_list *temp_list = result.config.path_select_list; - struct parameter_list *previous = NULL; - while (temp_list) { - if (temp_list->best_match) { - if (np_regex_match_mount_entry(temp_list->best_match, ®ex)) { + for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) { + if (elem->best_match) { + if (np_regex_match_mount_entry(elem->best_match, ®ex)) { if (verbose >= 3) { - printf("ignoring %s matching regex\n", temp_list->name); + printf("ignoring %s matching regex\n", elem->name); } - temp_list = np_del_parameter(temp_list, previous); - /* pointer to first element needs to be updated if first item gets deleted */ - if (previous == NULL) { - result.config.path_select_list = temp_list; - } - } else { - previous = temp_list; - temp_list = temp_list->name_next; + elem = mp_int_fs_list_del(&result.config.path_select_list, elem); + continue; } - } else { - previous = temp_list; - temp_list = temp_list->name_next; } + + elem = mp_int_fs_list_get_next(elem); } cflags = default_cflags; @@ -734,11 +679,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { cflags |= REG_ICASE; // Intentional fallthrough case 'r': { - if (!(result.config.warn_freespace_units || result.config.crit_freespace_units || result.config.warn_freespace_percent || - result.config.crit_freespace_percent || result.config.warn_usedspace_units || result.config.crit_usedspace_units || - result.config.warn_usedspace_percent || result.config.crit_usedspace_percent || result.config.warn_usedinodes_percent || - result.config.crit_usedinodes_percent || result.config.warn_freeinodes_percent || - result.config.crit_freeinodes_percent)) { + if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || + warn_freeinodes_percent || crit_freeinodes_percent)) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); } @@ -760,31 +702,28 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { } /* add parameter if not found. overwrite thresholds if path has already been added */ - struct parameter_list *se = NULL; - if (!(se = np_find_parameter(result.config.path_select_list, me->me_mountdir))) { - se = np_add_parameter(&result.config.path_select_list, me->me_mountdir); + parameter_list_elem *se = NULL; + if (!(se = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { + se = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); } - se->group = result.config.group; - set_all_thresholds(se, result.config.warn_freespace_units, result.config.crit_freespace_units, - result.config.warn_freespace_percent, result.config.crit_freespace_percent, - result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, - result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, - result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + se->group = group; + set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); } } - if (!found && result.config.ignore_missing) { - result.config.path_ignored = true; - result.config.path_selected = true; - break; - } if (!found) { + if (result.config.ignore_missing) { + result.config.path_ignored = true; + path_selected = true; + break; + } + die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); } - result.config.path_selected = true; - np_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); + path_selected = true; + mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); cflags = default_cflags; } break; @@ -793,37 +732,28 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { break; case 'C': { /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ - if (!result.config.path_selected) { - struct parameter_list *path; + if (!path_selected) { + parameter_list_elem *path; for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { - if (!(path = np_find_parameter(result.config.path_select_list, me->me_mountdir))) { - path = np_add_parameter(&result.config.path_select_list, me->me_mountdir); + if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { + path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); } path->best_match = me; - path->group = result.config.group; - set_all_thresholds(path, result.config.warn_freespace_units, result.config.crit_freespace_units, - result.config.warn_freespace_percent, result.config.crit_freespace_percent, - result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, - result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, - result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + path->group = group; + set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); } } - result.config.warn_freespace_units = NULL; - result.config.crit_freespace_units = NULL; - result.config.warn_usedspace_units = NULL; - result.config.crit_usedspace_units = NULL; - result.config.warn_freespace_percent = NULL; - result.config.crit_freespace_percent = NULL; - result.config.warn_usedspace_percent = NULL; - result.config.crit_usedspace_percent = NULL; - result.config.warn_usedinodes_percent = NULL; - result.config.crit_usedinodes_percent = NULL; - result.config.warn_freeinodes_percent = NULL; - result.config.crit_freeinodes_percent = NULL; - - result.config.path_selected = false; - result.config.group = NULL; + + warn_freespace_units = NULL; + crit_freespace_units = NULL; + warn_freespace_percent = NULL; + crit_freespace_percent = NULL; + warn_freeinodes_percent = NULL; + crit_freeinodes_percent = NULL; + + path_selected = false; + group = NULL; } break; case 'V': /* version */ print_revision(progname, NP_VERSION); @@ -833,68 +763,145 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { exit(STATE_UNKNOWN); case '?': /* help */ usage(_("Unknown argument")); + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } } } /* Support for "check_disk warn crit [fs]" with thresholds at used% level */ int index = optind; - if (result.config.warn_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (argc > index && is_intnonneg(argv[index])) { if (verbose > 0) { printf("Got an positional warn threshold: %s\n", argv[index]); } - result.config.warn_usedspace_percent = argv[index++]; + char *range = argv[index++]; + mp_range_parsed tmp = mp_parse_range_string(range); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold"); + } + + mp_range tmp_range = tmp.range; + // Invert range to use it for free instead of used + // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range; + + warn_freespace_percent = mp_range_to_string(tmp_range); + + if (verbose > 0) { + printf("Positional warning threshold transformed to: %s\n", warn_freespace_percent); + } } - if (result.config.crit_usedspace_percent == NULL && argc > index && is_intnonneg(argv[index])) { + if (argc > index && is_intnonneg(argv[index])) { if (verbose > 0) { printf("Got an positional crit threshold: %s\n", argv[index]); } - result.config.crit_usedspace_percent = argv[index++]; + char *range = argv[index++]; + mp_range_parsed tmp = mp_parse_range_string(range); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold"); + } + + mp_range tmp_range = tmp.range; + // Invert range to use it for free instead of used + // tmp_range.alert_on_inside_range = !tmp_range.alert_on_inside_range; + + crit_freespace_percent = mp_range_to_string(tmp_range); + + if (verbose > 0) { + printf("Positional critical threshold transformed to: %s\n", crit_freespace_percent); + } } if (argc > index) { if (verbose > 0) { printf("Got an positional filesystem: %s\n", argv[index]); } - struct parameter_list *se = np_add_parameter(&result.config.path_select_list, strdup(argv[index++])); - result.config.path_selected = true; - set_all_thresholds(se, result.config.warn_freespace_units, result.config.crit_freespace_units, result.config.warn_freespace_percent, - result.config.crit_freespace_percent, result.config.warn_usedspace_units, result.config.crit_usedspace_units, - result.config.warn_usedspace_percent, result.config.crit_usedspace_percent, - result.config.warn_usedinodes_percent, result.config.crit_usedinodes_percent, - result.config.warn_freeinodes_percent, result.config.crit_freeinodes_percent); + struct parameter_list *se = mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++])); + path_selected = true; + set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); } - if (result.config.units == NULL) { - result.config.units = strdup("MiB"); - result.config.mult = (uintmax_t)1024 * 1024; + // If a list of paths has not been explicitely selected, find entire + // mount list and create list of paths + if (!path_selected && !result.config.path_ignored) { + for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { + if (me->me_dummy != 0) { + // just do not add dummy filesystems + continue; + } + + parameter_list_elem *path = NULL; + if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { + path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); + } + path->best_match = me; + path->group = group; + set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + warn_freeinodes_percent, crit_freeinodes_percent); + } + } + + // Set thresholds to the appropriate unit + for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp; tmp = mp_int_fs_list_get_next(tmp)) { + + mp_perfdata_value factor = mp_create_pd_value(unit); + + if (tmp->freespace_units.critical_is_set) { + tmp->freespace_units.critical = mp_range_multiply(tmp->freespace_units.critical, factor); + } + if (tmp->freespace_units.warning_is_set) { + tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor); + } } return result; } -void set_all_thresholds(struct parameter_list *path, char *warn_freespace_units, char *crit_freespace_units, char *warn_freespace_percent, - char *crit_freespace_percent, char *warn_usedspace_units, char *crit_usedspace_units, char *warn_usedspace_percent, - char *crit_usedspace_percent, char *warn_usedinodes_percent, char *crit_usedinodes_percent, - char *warn_freeinodes_percent, char *crit_freeinodes_percent) { - free(path->freespace_units); - set_thresholds(&path->freespace_units, warn_freespace_units, crit_freespace_units); +void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units, char *warn_freespace_percent, + char *crit_freespace_percent, char *warn_freeinodes_percent, char *crit_freeinodes_percent) { + mp_range_parsed tmp; + + if (warn_freespace_units) { + tmp = mp_parse_range_string(warn_freespace_units); + path->freespace_units = mp_thresholds_set_warn(path->freespace_units, tmp.range); + } - free(path->freespace_percent); - set_thresholds(&path->freespace_percent, warn_freespace_percent, crit_freespace_percent); + if (crit_freespace_units) { + tmp = mp_parse_range_string(crit_freespace_units); + path->freespace_units = mp_thresholds_set_crit(path->freespace_units, tmp.range); + } - free(path->usedspace_units); - set_thresholds(&path->usedspace_units, warn_usedspace_units, crit_usedspace_units); + if (warn_freespace_percent) { + tmp = mp_parse_range_string(warn_freespace_percent); + path->freespace_percent = mp_thresholds_set_warn(path->freespace_percent, tmp.range); + } - free(path->usedspace_percent); - set_thresholds(&path->usedspace_percent, warn_usedspace_percent, crit_usedspace_percent); + if (crit_freespace_percent) { + tmp = mp_parse_range_string(crit_freespace_percent); + path->freespace_percent = mp_thresholds_set_crit(path->freespace_percent, tmp.range); + } - free(path->usedinodes_percent); - set_thresholds(&path->usedinodes_percent, warn_usedinodes_percent, crit_usedinodes_percent); + if (warn_freeinodes_percent) { + tmp = mp_parse_range_string(warn_freeinodes_percent); + path->freeinodes_percent = mp_thresholds_set_warn(path->freeinodes_percent, tmp.range); + } - free(path->freeinodes_percent); - set_thresholds(&path->freeinodes_percent, warn_freeinodes_percent, crit_freeinodes_percent); + if (crit_freeinodes_percent) { + tmp = mp_parse_range_string(crit_freeinodes_percent); + path->freeinodes_percent = mp_thresholds_set_crit(path->freeinodes_percent, tmp.range); + } } void print_help(void) { @@ -941,8 +948,6 @@ void print_help(void) { printf(" %s\n", _("Display inode usage in perfdata")); printf(" %s\n", "-g, --group=NAME"); printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); - printf(" %s\n", "-k, --kilobytes"); - printf(" %s\n", _("Same as '--units kB'")); printf(" %s\n", "-l, --local"); printf(" %s\n", _("Only check local filesystems")); printf(" %s\n", "-L, --stat-remote-fs"); @@ -950,8 +955,6 @@ void print_help(void) { printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); printf(" %s\n", "-M, --mountpoint"); printf(" %s\n", _("Display the (block) device instead of the mount point")); - printf(" %s\n", "-m, --megabytes"); - printf(" %s\n", _("Same as '--units MB'")); printf(" %s\n", "-A, --all"); printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); @@ -967,12 +970,25 @@ void print_help(void) { printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(" %s\n", "-u, --units=STRING"); - printf(" %s\n", _("Choose bytes, kB, MB, GB, TB (default: MB)")); + printf(" %s\n", _("Select the unit used for the absolute value thresholds")); + printf( + " %s\n", + _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); + printf(" %s\n", "-k, --kilobytes"); + printf(" %s\n", _("Same as '--units kB'")); + printf(" %s\n", "--display-unit"); + printf(" %s\n", _("Select the unit used for in the output")); + printf( + " %s\n", + _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); + printf(" %s\n", "-m, --megabytes"); + printf(" %s\n", _("Same as '--units MB'")); printf(UT_VERBOSE); printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); printf(" %s\n", "-N, --include-type=TYPE_REGEX"); printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); + printf(UT_OUTPUT_FORMAT); printf("\n"); printf("%s\n", _("General usage hints:")); @@ -1002,7 +1018,7 @@ void print_usage(void) { printf("[-t timeout] [-u unit] [-v] [-X type_regex] [-N type]\n"); } -bool stat_path(struct parameter_list *parameters, bool ignore_missing) { +bool stat_path(parameter_list_elem *parameters, bool ignore_missing) { /* Stat entry to check that dir exists and is accessible */ if (verbose >= 3) { printf("calling stat on %s\n", parameters->name); @@ -1023,97 +1039,166 @@ bool stat_path(struct parameter_list *parameters, bool ignore_missing) { return true; } -void get_stats(struct parameter_list *parameters, struct fs_usage *fsp, bool ignore_missing, bool freespace_ignore_reserved, uintmax_t mult, - struct parameter_list *path_select_list, struct name_list *seen) { - struct fs_usage tmpfsp; - bool first = true; - - if (parameters->group == NULL) { - get_path_stats(parameters, fsp, freespace_ignore_reserved, mult, seen); - } else { - /* find all group members */ - for (struct parameter_list *p_list = path_select_list; p_list; p_list = p_list->name_next) { - -#ifdef __CYGWIN__ - if (strncmp(p_list->name, "/cygdrive/", 10) != 0) { - continue; - } -#endif - - if (p_list->group && !(strcmp(p_list->group, parameters->group))) { - if (!stat_path(p_list, ignore_missing)) { - continue; - } - get_fs_usage(p_list->best_match->me_mountdir, p_list->best_match->me_devname, &tmpfsp); - get_path_stats(p_list, &tmpfsp, freespace_ignore_reserved, mult, seen); - if (verbose >= 3) { - printf("Group %s: adding %lu blocks sized %lu, (%s) used_units=%lu free_units=%lu total_units=%lu mult=%lu\n", - p_list->group, tmpfsp.fsu_blocks, tmpfsp.fsu_blocksize, p_list->best_match->me_mountdir, p_list->dused_units, - p_list->dfree_units, p_list->dtotal_units, mult); - } - - /* prevent counting the first FS of a group twice since its parameter_list entry - * is used to carry the information of all file systems of the entire group */ - if (!first) { - parameters->total += p_list->total; - parameters->available += p_list->available; - parameters->available_to_root += p_list->available_to_root; - parameters->used += p_list->used; - - parameters->dused_units += p_list->dused_units; - parameters->dfree_units += p_list->dfree_units; - parameters->dtotal_units += p_list->dtotal_units; - parameters->inodes_total += p_list->inodes_total; - parameters->inodes_free += p_list->inodes_free; - parameters->inodes_free_to_root += p_list->inodes_free_to_root; - parameters->inodes_used += p_list->inodes_used; - } - first = false; - } - if (verbose >= 3) { - printf("Group %s now has: used_units=%lu free_units=%lu total_units=%lu fsu_blocksize=%lu mult=%lu\n", parameters->group, - parameters->dused_units, parameters->dfree_units, parameters->dtotal_units, tmpfsp.fsu_blocksize, mult); - } - } - /* modify devname and mountdir for output */ - parameters->best_match->me_mountdir = parameters->best_match->me_devname = parameters->group; - } - /* finally calculate percentages for either plain FS or summed up group */ - parameters->dused_pct = - calculate_percent(parameters->used, parameters->used + parameters->available); /* used + available can never be > uintmax */ - parameters->dfree_pct = 100.0 - parameters->dused_pct; - parameters->dused_inodes_percent = calculate_percent(parameters->inodes_total - parameters->inodes_free, parameters->inodes_total); - parameters->dfree_inodes_percent = 100 - parameters->dused_inodes_percent; -} +static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp, bool freespace_ignore_reserved) { + uintmax_t available = fsp.fsu_bavail; + uintmax_t available_to_root = fsp.fsu_bfree; + uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree; + uintmax_t total; -void get_path_stats(struct parameter_list *parameters, struct fs_usage *fsp, bool freespace_ignore_reserved, uintmax_t mult, - struct name_list *seen) { - parameters->available = fsp->fsu_bavail; - parameters->available_to_root = fsp->fsu_bfree; - parameters->used = fsp->fsu_blocks - fsp->fsu_bfree; if (freespace_ignore_reserved) { /* option activated : we subtract the root-reserved space from the total */ - parameters->total = fsp->fsu_blocks - parameters->available_to_root + parameters->available; + total = fsp.fsu_blocks - available_to_root + available; } else { /* default behaviour : take all the blocks into account */ - parameters->total = fsp->fsu_blocks; + total = fsp.fsu_blocks; } - parameters->dused_units = parameters->used * fsp->fsu_blocksize / mult; - parameters->dfree_units = parameters->available * fsp->fsu_blocksize / mult; - parameters->dtotal_units = parameters->total * fsp->fsu_blocksize / mult; + parameters.used_bytes = used * fsp.fsu_blocksize; + parameters.free_bytes = available * fsp.fsu_blocksize; + parameters.total_bytes = total * fsp.fsu_blocksize; + /* Free file nodes. Not sure the workaround is required, but in case...*/ - parameters->inodes_free = fsp->fsu_ffree; - parameters->inodes_free_to_root = fsp->fsu_ffree; /* Free file nodes for root. */ - parameters->inodes_used = fsp->fsu_files - fsp->fsu_ffree; + parameters.inodes_free = fsp.fsu_ffree; + parameters.inodes_free_to_root = fsp.fsu_ffree; /* Free file nodes for root. */ + parameters.inodes_used = fsp.fsu_files - fsp.fsu_ffree; + if (freespace_ignore_reserved) { /* option activated : we subtract the root-reserved inodes from the total */ /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ /* for others, fsp->fsu_ffree == fsp->fsu_favail */ - parameters->inodes_total = fsp->fsu_files - parameters->inodes_free_to_root + parameters->inodes_free; + parameters.inodes_total = fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free; } else { /* default behaviour : take all the inodes into account */ - parameters->inodes_total = fsp->fsu_files; + parameters.inodes_total = fsp.fsu_files; + } + + return parameters; +} + +mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit) { + mp_subcheck result = mp_subcheck_init(); + result = mp_set_subcheck_default_state(result, STATE_UNKNOWN); + xasprintf(&result.output, "%s", measurement_unit.name); + + if (!measurement_unit.is_group && measurement_unit.filesystem_type) { + xasprintf(&result.output, "%s (%s)", result.output, measurement_unit.filesystem_type); + } + + /* Threshold comparisons */ + + // =============================== + // Free space absolute values test + mp_subcheck freespace_bytes_sc = mp_subcheck_init(); + freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK); + + if (unit != Humanized) { + xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)", (uintmax_t)(measurement_unit.free_bytes / unit), + get_unit_string(unit), (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit)); + } else { + xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)", humanize_byte_value(measurement_unit.free_bytes, false), + humanize_byte_value(measurement_unit.total_bytes, false)); } - np_add_name(&seen, parameters->best_match->me_mountdir); + + mp_perfdata used_space = perfdata_init(); + used_space.label = measurement_unit.name; + used_space.value = mp_create_pd_value(measurement_unit.free_bytes); + used_space = mp_set_pd_max_value(used_space, mp_create_pd_value(measurement_unit.total_bytes)); + used_space = mp_set_pd_min_value(used_space, mp_create_pd_value(0)); + used_space.uom = "B"; + used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); + freespace_bytes_sc = mp_set_subcheck_state(freespace_bytes_sc, mp_get_pd_status(used_space)); + + // special case for absolute space thresholds here: + // if absolute values are not set, compute the thresholds from percentage thresholds + mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds; + if (!temp_thlds.critical_is_set && measurement_unit.freespace_percent_thresholds.critical_is_set) { + mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical; + + if (!tmp_range.end_infinity) { + tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes); + } + + if (!tmp_range.start_infinity) { + tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes); + } + measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range); + used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); + } + + if (!temp_thlds.warning_is_set && measurement_unit.freespace_percent_thresholds.warning_is_set) { + mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning; + if (!tmp_range.end_infinity) { + tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes); + } + if (!tmp_range.start_infinity) { + tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes); + } + measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range); + used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); + } + + mp_add_perfdata_to_subcheck(&freespace_bytes_sc, used_space); + mp_add_subcheck_to_subcheck(&result, freespace_bytes_sc); + + // ========================== + // Free space percentage test + mp_subcheck freespace_percent_sc = mp_subcheck_init(); + freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK); + + double free_percentage = calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes); + xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage); + + // Using perfdata here just to get to the test result + mp_perfdata free_space_percent_pd = perfdata_init(); + free_space_percent_pd.value = mp_create_pd_value(free_percentage); + free_space_percent_pd = mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds); + + freespace_percent_sc = mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd)); + mp_add_subcheck_to_subcheck(&result, freespace_percent_sc); + + // ================ + // Free inodes test + // Only ever useful if the number of inodes is static (e.g. ext4), + // not when it is dynamic (e.g btrfs) + // Assumption: if the total number of inodes == 0, we have such a case and just skip the test + if (measurement_unit.inodes_total > 0) { + mp_subcheck freeindodes_percent_sc = mp_subcheck_init(); + freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK); + + double free_inode_percentage = calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total); + + if (verbose > 0) { + printf("free inode percentage computed: %g\n", free_inode_percentage); + } + + xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)", free_inode_percentage, measurement_unit.inodes_free, + measurement_unit.inodes_total); + + mp_perfdata inodes_pd = perfdata_init(); + xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name); + inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used); + inodes_pd = mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total)); + inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0)); + + mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds; + + if (absolut_inode_thresholds.critical_is_set) { + absolut_inode_thresholds.critical = + mp_range_multiply(absolut_inode_thresholds.critical, mp_create_pd_value(measurement_unit.inodes_total / 100)); + } + if (absolut_inode_thresholds.warning_is_set) { + absolut_inode_thresholds.warning = + mp_range_multiply(absolut_inode_thresholds.warning, mp_create_pd_value(measurement_unit.inodes_total / 100)); + } + + inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds); + + freeindodes_percent_sc = mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd)); + if (display_inodes_perfdata) { + mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd); + } + mp_add_subcheck_to_subcheck(&result, freeindodes_percent_sc); + } + + return result; } diff --git a/plugins/check_disk.d/config.h b/plugins/check_disk.d/config.h deleted file mode 100644 index d890fc1a..00000000 --- a/plugins/check_disk.d/config.h +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -#include "../../config.h" -#include -#include - -typedef struct { - // Output options - bool erronly; - bool display_mntp; - /* show only local filesystems. */ - bool show_local_fs; - /* show only local filesystems but call stat() on remote ones. */ - bool stat_remote_fs; - bool display_inodes_perfdata; - - bool exact_match; - bool ignore_missing; - bool path_ignored; - bool path_selected; - bool freespace_ignore_reserved; - - char *warn_freespace_units; - char *crit_freespace_units; - char *warn_freespace_percent; - char *crit_freespace_percent; - char *warn_usedspace_units; - char *crit_usedspace_units; - char *warn_usedspace_percent; - char *crit_usedspace_percent; - char *warn_usedinodes_percent; - char *crit_usedinodes_percent; - char *warn_freeinodes_percent; - char *crit_freeinodes_percent; - - /* Linked list of filesystem types to omit. - If the list is empty, don't exclude any types. */ - struct regex_list *fs_exclude_list; - /* Linked list of filesystem types to check. - If the list is empty, include all types. */ - struct regex_list *fs_include_list; - struct name_list *device_path_exclude_list; - struct parameter_list *path_select_list; - /* Linked list of mounted filesystems. */ - struct mount_entry *mount_list; - struct name_list *seen; - - char *units; - uintmax_t mult; - char *group; -} check_disk_config; - -check_disk_config check_disk_config_init() { - check_disk_config tmp = { - .erronly = false, - .display_mntp = false, - .show_local_fs = false, - .stat_remote_fs = false, - .display_inodes_perfdata = false, - - .exact_match = false, - .ignore_missing = false, - .path_ignored = false, - .path_selected = false, - .freespace_ignore_reserved = false, - - .warn_freespace_units = NULL, - .crit_freespace_units = NULL, - .warn_freespace_percent = NULL, - .crit_freespace_percent = NULL, - .warn_usedspace_units = NULL, - .crit_usedspace_units = NULL, - .warn_usedspace_percent = NULL, - .crit_usedspace_percent = NULL, - .warn_usedinodes_percent = NULL, - .crit_usedinodes_percent = NULL, - .warn_freeinodes_percent = NULL, - .crit_freeinodes_percent = NULL, - - .fs_exclude_list = NULL, - .fs_include_list = NULL, - .device_path_exclude_list = NULL, - .path_select_list = NULL, - .mount_list = NULL, - .seen = NULL, - - .units = NULL, - .mult = 1024 * 1024, - .group = NULL, - }; - return tmp; -} diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c index 369c85d5..3986a8a8 100644 --- a/plugins/check_disk.d/utils_disk.c +++ b/plugins/check_disk.d/utils_disk.c @@ -29,7 +29,12 @@ #include "../common.h" #include "utils_disk.h" #include "../../gl/fsusage.h" +#include "../../lib/thresholds.h" +#include "../../lib/states.h" +#include +#include #include +#include void np_add_name(struct name_list **list, const char *name) { struct name_list *new_entry; @@ -70,82 +75,367 @@ int np_add_regex(struct regex_list **list, const char *regex, int cflags) { return regcomp_result; } -struct parameter_list parameter_list_init(const char *name) { - struct parameter_list result = { +parameter_list_elem parameter_list_init(const char *name) { + parameter_list_elem result = { .name = strdup(name), .best_match = NULL, - .name_next = NULL, - .name_prev = NULL, - - .freespace_units = NULL, - .freespace_percent = NULL, - .usedspace_units = NULL, - .usedspace_percent = NULL, - .usedinodes_percent = NULL, - .freeinodes_percent = NULL, + .freespace_units = mp_thresholds_init(), + .freespace_percent = mp_thresholds_init(), + .freeinodes_percent = mp_thresholds_init(), .group = NULL, - .dfree_pct = -1, - .dused_pct = -1, - .total = 0, - .available = 0, - .available_to_root = 0, - .used = 0, - .dused_units = 0, - .dfree_units = 0, - .dtotal_units = 0, + .inodes_total = 0, .inodes_free = 0, .inodes_free_to_root = 0, .inodes_used = 0, - .dused_inodes_percent = 0, - .dfree_inodes_percent = 0, + + .used_bytes = 0, + .free_bytes = 0, + .total_bytes = 0, + + .next = NULL, + .prev = NULL, }; return result; } -/* Initialises a new parameter at the end of list */ -struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name) { - struct parameter_list *current = *list; - struct parameter_list *new_path; - new_path = (struct parameter_list *)malloc(sizeof *new_path); +/* Returns true if name is in list */ +bool np_find_name(struct name_list *list, const char *name) { + if (list == NULL || name == NULL) { + return false; + } + for (struct name_list *iterator = list; iterator; iterator = iterator->next) { + if (!strcmp(name, iterator->name)) { + return true; + } + } + return false; +} + +/* Returns true if name is in list */ +bool np_find_regmatch(struct regex_list *list, const char *name) { + if (name == NULL) { + return false; + } + + size_t len = strlen(name); + + for (; list; list = list->next) { + /* Emulate a full match as if surrounded with ^( )$ + by checking whether the match spans the whole name */ + regmatch_t dummy_match; + if (!regexec(&list->regex, name, 1, &dummy_match, 0) && dummy_match.rm_so == 0 && dummy_match.rm_eo == len) { + return true; + } + } + + return false; +} + +bool np_seen_name(struct name_list *list, const char *name) { + for (struct name_list *iterator = list; iterator; iterator = iterator->next) { + if (!strcmp(iterator->name, name)) { + return true; + } + } + return false; +} + +bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { + return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0)); +} + +check_disk_config check_disk_config_init() { + check_disk_config tmp = { + .erronly = false, + .display_mntp = false, + .show_local_fs = false, + .stat_remote_fs = false, + .display_inodes_perfdata = false, + + .exact_match = false, + .freespace_ignore_reserved = false, + .ignore_missing = false, + .path_ignored = false, + + // FS Filters + .fs_exclude_list = NULL, + .fs_include_list = NULL, + .device_path_exclude_list = NULL, + + // Actual filesystems paths to investigate + .path_select_list = filesystem_list_init(), + + .mount_list = NULL, + .seen = NULL, + + .display_unit = Humanized, + // .unit = MebiBytes, + + .output_format_is_set = false, + }; + return tmp; +} + +char *get_unit_string(byte_unit unit) { + switch (unit) { + case Bytes: + return "Bytes"; + case KibiBytes: + return "KiB"; + case MebiBytes: + return "MiB"; + case GibiBytes: + return "GiB"; + case TebiBytes: + return "TiB"; + case PebiBytes: + return "PiB"; + case ExbiBytes: + return "EiB"; + case KiloBytes: + return "KB"; + case MegaBytes: + return "MB"; + case GigaBytes: + return "GB"; + case TeraBytes: + return "TB"; + case PetaBytes: + return "PB"; + case ExaBytes: + return "EB"; + default: + assert(false); + } +} + +measurement_unit measurement_unit_init() { + measurement_unit tmp = { + .name = NULL, + .filesystem_type = NULL, + .is_group = false, + + .freeinodes_percent_thresholds = mp_thresholds_init(), + .freespace_percent_thresholds = mp_thresholds_init(), + .freespace_bytes_thresholds = mp_thresholds_init(), + + .free_bytes = 0, + .used_bytes = 0, + .total_bytes = 0, + + .inodes_total = 0, + .inodes_free = 0, + .inodes_free_to_root = 0, + .inodes_used = 0, + }; + return tmp; +} + +// Add a given element to the list, memory for the new element is freshly allocated +// Returns a pointer to new element +measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem) { + // find last element + measurement_unit_list *new = NULL; + if (list == NULL) { + new = calloc(1, sizeof(measurement_unit_list)); + if (new == NULL) { + die(STATE_UNKNOWN, _("allocation failed")); + } + } else { + measurement_unit_list *list_elem = list; + while (list_elem->next != NULL) { + list_elem = list_elem->next; + } + + new = calloc(1, sizeof(measurement_unit_list)); + if (new == NULL) { + die(STATE_UNKNOWN, _("allocation failed")); + } + + list_elem->next = new; + } + + new->unit = elem; + new->next = NULL; + return new; +} + +measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, parameter_list_elem filesystem) { + + unit.free_bytes += filesystem.free_bytes; + unit.used_bytes += filesystem.used_bytes; + unit.total_bytes += filesystem.total_bytes; + + unit.inodes_total += filesystem.inodes_total; + unit.inodes_free += filesystem.inodes_free; + unit.inodes_free_to_root += filesystem.inodes_free_to_root; + unit.inodes_used += filesystem.inodes_used; + return unit; +} + +measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, bool display_mntp) { + measurement_unit result = measurement_unit_init(); + if (!display_mntp) { + result.name = strdup(filesystem.best_match->me_mountdir); + } else { + result.name = strdup(filesystem.best_match->me_devname); + } + + if (filesystem.group) { + result.is_group = true; + } else { + result.is_group = false; + if (filesystem.best_match) { + result.filesystem_type = filesystem.best_match->me_type; + } + } + + result.freeinodes_percent_thresholds = filesystem.freeinodes_percent; + result.freespace_percent_thresholds = filesystem.freespace_percent; + result.freespace_bytes_thresholds = filesystem.freespace_units; + result.free_bytes = filesystem.free_bytes; + result.total_bytes = filesystem.total_bytes; + result.used_bytes = filesystem.used_bytes; + result.inodes_total = filesystem.inodes_total; + result.inodes_used = filesystem.inodes_used; + result.inodes_free = filesystem.inodes_free; + result.inodes_free_to_root = filesystem.inodes_free_to_root; + return result; +} + +#define RANDOM_STRING_LENGTH 64 + +char *humanize_byte_value(uintmax_t value, bool use_si_units) { + // Idea: A reasonable output should have at most 3 orders of magnitude + // before the decimal separator + // 353GiB is ok, 2444 GiB should be 2.386 TiB + char *result = calloc(RANDOM_STRING_LENGTH, sizeof(char)); + if (result == NULL) { + die(STATE_UNKNOWN, _("allocation failed")); + } + + if (use_si_units) { + // SI units, powers of 10 + if (value < KiloBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju B", value); + } else if (value < MegaBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju KB", value / KiloBytes); + } else if (value < GigaBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju MB", value / MegaBytes); + } else if (value < TeraBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju GB", value / GigaBytes); + } else if (value < PetaBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju TB", value / TeraBytes); + } else { + snprintf(result, RANDOM_STRING_LENGTH, "%ju PB", value / PetaBytes); + } + } else { + // IEC units, powers of 2 ^ 10 + if (value < KibiBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju B", value); + } else if (value < MebiBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju KiB", value / KibiBytes); + } else if (value < GibiBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju MiB", value / MebiBytes); + } else if (value < TebiBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju GiB", value / GibiBytes); + } else if (value < PebiBytes) { + snprintf(result, RANDOM_STRING_LENGTH, "%ju TiB", value / TebiBytes); + } else { + snprintf(result, RANDOM_STRING_LENGTH, "%ju PiB", value / PebiBytes); + } + } + + return result; +} + +filesystem_list filesystem_list_init() { + filesystem_list tmp = { + .length = 0, + .first = NULL, + }; + return tmp; +} + +parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name) { + parameter_list_elem *current = list->first; + parameter_list_elem *new_path = (struct parameter_list *)malloc(sizeof *new_path); *new_path = parameter_list_init(name); if (current == NULL) { - *list = new_path; - new_path->name_prev = NULL; + list->first = new_path; + new_path->prev = NULL; + list->length = 1; } else { - while (current->name_next) { - current = current->name_next; + while (current->next) { + current = current->next; } - current->name_next = new_path; - new_path->name_prev = current; + current->next = new_path; + new_path->prev = current; + list->length++; } return new_path; } -/* Delete a given parameter from list and return pointer to next element*/ -struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev) { - if (item == NULL) { +parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name) { + if (list.length == 0) { return NULL; } - struct parameter_list *next; + for (parameter_list_elem *temp_list = list.first; temp_list; temp_list = temp_list->next) { + if (!strcmp(temp_list->name, name)) { + return temp_list; + } + } - if (item->name_next) { - next = item->name_next; - } else { - next = NULL; + return NULL; +} + +parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item) { + if (list->length == 0) { + return NULL; } - if (next) { - next->name_prev = prev; + if (item == NULL) { + // Got NULL for item, interpret this as "delete first element" + // as a kind of compatibility to the old function + item = list->first; } - if (prev) { - prev->name_next = next; + if (list->first == item) { + list->length--; + + list->first = item->next; + if (list->first) { + list->first->prev = NULL; + } + return list->first; + } + + // Was not the first element, continue + parameter_list_elem *prev = list->first; + parameter_list_elem *current = list->first->next; + + while (current != item && current != NULL) { + prev = current; + current = current->next; + } + + if (current == NULL) { + // didn't find that element .... + return NULL; + } + + // remove the element + parameter_list_elem *next = current->next; + prev->next = next; + list->length--; + if (next) { + next->prev = prev; } if (item->name) { @@ -156,29 +446,23 @@ struct parameter_list *np_del_parameter(struct parameter_list *item, struct para return next; } -/* returns a pointer to the struct found in the list */ -struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name) { - for (struct parameter_list *temp_list = list; temp_list; temp_list = temp_list->name_next) { - if (!strcmp(temp_list->name, name)) { - return temp_list; - } +parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current) { + if (!current) { + return NULL; } - - return NULL; + return current->next; } -void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact) { - for (struct parameter_list *d = desired; d; d = d->name_next) { - if (!d->best_match) { - struct mount_entry *mount_entry; - size_t name_len = strlen(d->name); - size_t best_match_len = 0; +void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, bool exact) { + for (parameter_list_elem *elem = list.first; elem; elem = mp_int_fs_list_get_next(elem)) { + if (!elem->best_match) { + size_t name_len = strlen(elem->name); struct mount_entry *best_match = NULL; - struct fs_usage fsp; /* set best match if path name exactly matches a mounted device name */ - for (mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { - if (strcmp(mount_entry->me_devname, d->name) == 0) { + for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { + if (strcmp(mount_entry->me_devname, elem->name) == 0) { + struct fs_usage fsp; if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) { best_match = mount_entry; } @@ -187,11 +471,15 @@ void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount /* set best match by directory name if no match was found by devname */ if (!best_match) { - for (mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { + size_t best_match_len = 0; + for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { size_t len = strlen(mount_entry->me_mountdir); + if ((!exact && (best_match_len <= len && len <= name_len && - (len == 1 || strncmp(mount_entry->me_mountdir, d->name, len) == 0))) || - (exact && strcmp(mount_entry->me_mountdir, d->name) == 0)) { + (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) || + (exact && strcmp(mount_entry->me_mountdir, elem->name) == 0)) { + struct fs_usage fsp; + if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) { best_match = mount_entry; best_match_len = len; @@ -201,56 +489,13 @@ void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount } if (best_match) { - d->best_match = best_match; + elem->best_match = best_match; } else { - d->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */ + elem->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */ } - } - } -} -/* Returns true if name is in list */ -bool np_find_name(struct name_list *list, const char *name) { - if (list == NULL || name == NULL) { - return false; - } - for (struct name_list *n = list; n; n = n->next) { - if (!strcmp(name, n->name)) { - return true; + // No filesystem without a mount_entry! + // assert(elem->best_match != NULL); } } - return false; -} - -/* Returns true if name is in list */ -bool np_find_regmatch(struct regex_list *list, const char *name) { - if (name == NULL) { - return false; - } - - int len = strlen(name); - - for (; list; list = list->next) { - /* Emulate a full match as if surrounded with ^( )$ - by checking whether the match spans the whole name */ - regmatch_t m; - if (!regexec(&list->regex, name, 1, &m, 0) && m.rm_so == 0 && m.rm_eo == len) { - return true; - } - } - - return false; -} - -bool np_seen_name(struct name_list *list, const char *name) { - for (struct name_list *s = list; s; s = s->next) { - if (!strcmp(s->name, name)) { - return true; - } - } - return false; -} - -bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { - return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0)); } diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h index 0c69f987..a66453ea 100644 --- a/plugins/check_disk.d/utils_disk.h +++ b/plugins/check_disk.d/utils_disk.h @@ -1,14 +1,34 @@ +#pragma once /* Header file for utils_disk */ #include "../../config.h" #include "../../gl/mountlist.h" #include "../../lib/utils_base.h" +#include "../../lib/output.h" #include "regex.h" #include +typedef enum : unsigned long { + Humanized = 0, + Bytes = 1, + KibiBytes = 1024, + MebiBytes = 1024 * KibiBytes, + GibiBytes = 1024 * MebiBytes, + TebiBytes = 1024 * GibiBytes, + PebiBytes = 1024 * TebiBytes, + ExbiBytes = 1024 * PebiBytes, + KiloBytes = 1000, + MegaBytes = 1000 * KiloBytes, + GigaBytes = 1000 * MegaBytes, + TeraBytes = 1000 * GigaBytes, + PetaBytes = 1000 * TeraBytes, + ExaBytes = 1000 * PetaBytes +} byte_unit; + +typedef struct name_list string_list; struct name_list { char *name; - struct name_list *next; + string_list *next; }; struct regex_list { @@ -16,54 +36,120 @@ struct regex_list { struct regex_list *next; }; +typedef struct parameter_list parameter_list_elem; struct parameter_list { char *name; char *group; - thresholds *freespace_units; - thresholds *freespace_percent; - thresholds *usedspace_units; - thresholds *usedspace_percent; - - thresholds *usedinodes_percent; - thresholds *freeinodes_percent; + mp_thresholds freespace_units; + mp_thresholds freespace_percent; + mp_thresholds freeinodes_percent; struct mount_entry *best_match; - uintmax_t total; - uintmax_t available; - uintmax_t available_to_root; - uintmax_t used; - uintmax_t inodes_free; uintmax_t inodes_free_to_root; + uintmax_t inodes_free; uintmax_t inodes_used; uintmax_t inodes_total; - double dfree_pct; - double dused_pct; + uint64_t used_bytes; + uint64_t free_bytes; + uint64_t total_bytes; - uint64_t dused_units; - uint64_t dfree_units; - uint64_t dtotal_units; + parameter_list_elem *next; + parameter_list_elem *prev; +}; + +typedef struct { + size_t length; + parameter_list_elem *first; +} filesystem_list; - double dused_inodes_percent; - double dfree_inodes_percent; +filesystem_list filesystem_list_init(); - struct parameter_list *name_next; - struct parameter_list *name_prev; +typedef struct { + char *name; + char *filesystem_type; + bool is_group; + + mp_thresholds freespace_bytes_thresholds; + mp_thresholds freespace_percent_thresholds; + mp_thresholds freeinodes_percent_thresholds; + + uintmax_t inodes_free_to_root; + uintmax_t inodes_free; + uintmax_t inodes_used; + uintmax_t inodes_total; + + uintmax_t used_bytes; + uintmax_t free_bytes; + uintmax_t total_bytes; +} measurement_unit; + +typedef struct measurement_unit_list measurement_unit_list; +struct measurement_unit_list { + measurement_unit unit; + measurement_unit_list *next; }; +typedef struct { + // Output options + bool erronly; + bool display_mntp; + /* show only local filesystems. */ + bool show_local_fs; + /* show only local filesystems but call stat() on remote ones. */ + bool stat_remote_fs; + bool display_inodes_perfdata; + + bool exact_match; + bool freespace_ignore_reserved; + + bool ignore_missing; + bool path_ignored; + + /* Linked list of filesystem types to omit. + If the list is empty, don't exclude any types. */ + struct regex_list *fs_exclude_list; + /* Linked list of filesystem types to check. + If the list is empty, include all types. */ + struct regex_list *fs_include_list; + struct name_list *device_path_exclude_list; + filesystem_list path_select_list; + /* Linked list of mounted filesystems. */ + struct mount_entry *mount_list; + struct name_list *seen; + + byte_unit display_unit; + // byte_unit unit; + + bool output_format_is_set; + mp_output_format output_format; +} check_disk_config; + void np_add_name(struct name_list **list, const char *name); bool np_find_name(struct name_list *list, const char *name); bool np_seen_name(struct name_list *list, const char *name); int np_add_regex(struct regex_list **list, const char *regex, int cflags); bool np_find_regmatch(struct regex_list *list, const char *name); -struct parameter_list *np_add_parameter(struct parameter_list **list, const char *name); -struct parameter_list *np_find_parameter(struct parameter_list *list, const char *name); -struct parameter_list *np_del_parameter(struct parameter_list *item, struct parameter_list *prev); -struct parameter_list parameter_list_init(const char *); +parameter_list_elem parameter_list_init(const char *); + +parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *name); +parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name); +parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item); +parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current); +void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, bool exact); -int search_parameter_list(struct parameter_list *list, const char *name); -void np_set_best_match(struct parameter_list *desired, struct mount_entry *mount_list, bool exact); +measurement_unit measurement_unit_init(); +measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem); +measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, parameter_list_elem filesystem); +measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, bool display_mntp); + +int search_parameter_list(parameter_list_elem *list, const char *name); bool np_regex_match_mount_entry(struct mount_entry *, regex_t *); + +char *get_unit_string(byte_unit); +check_disk_config check_disk_config_init(); + +char *humanize_byte_value(uintmax_t value, bool use_si_units); -- cgit v1.2.3-74-g34f1 From 76971dea753d52d3e177aa84605d9b239a3a793e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:38:12 +0200 Subject: Address check_disk changes in tests --- plugins/t/check_disk.t | 205 +++++++++++++++++++++++----------------- plugins/tests/test_check_disk.c | 67 ++++++------- 2 files changed, 154 insertions(+), 118 deletions(-) diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t index 9eb77ce4..16daee9a 100644 --- a/plugins/t/check_disk.t +++ b/plugins/t/check_disk.t @@ -10,6 +10,7 @@ use strict; use Test::More; use NPTest; use POSIX qw(ceil floor); +use Data::Dumper; my $successOutput = '/^DISK OK/'; my $failureOutput = '/^DISK CRITICAL/'; @@ -20,117 +21,148 @@ my $result; my $mountpoint_valid = getTestParameter( "NP_MOUNTPOINT_VALID", "Path to valid mountpoint", "/"); my $mountpoint2_valid = getTestParameter( "NP_MOUNTPOINT2_VALID", "Path to another valid mountpoint. Must be different from 1st one", "/var"); +my $output_format = "--output-format mp-test-json"; + if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") { plan skip_all => "Need 2 mountpoints to test"; } else { - plan tests => 94; + plan tests => 96; } $result = NPTest->testCmd( - "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -p $mountpoint2_valid" + "./check_disk -w 1% -c 1% -p $mountpoint_valid -w 1% -c 1% -P -p $mountpoint2_valid $output_format" ); cmp_ok( $result->return_code, "==", 0, "Checking two mountpoints (must have at least 1% free in space and inodes)"); -my $c = 0; -$_ = $result->output; -$c++ while /\(/g; # counts number of "(" - should be two -cmp_ok( $c, '==', 2, "Got two mountpoints in output"); +like($result->{'mp_test_result'}->{'state'}, "/OK/", "Main result is OK"); +like($result->{'mp_test_result'}->{'checks'}->[0]->{'state'}, "/OK/", "First sub result is OK"); +like($result->{'mp_test_result'}->{'checks'}->[1]->{'state'}, "/OK/", "Second sub result is OK"); + +my $absolut_space_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'}; +# print("absolut space on mp1: ". $absolut_space_mp1 . "\n"); + +my $free_percent_on_mp1 = ($result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / ($absolut_space_mp1/100)); +print("free percent on mp1: ". $free_percent_on_mp1 . "\n"); + +my $absolut_space_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'}; +# print("absolut space on mp2: ". $absolut_space_mp2 . "\n"); -# Get perf data -# Should use Monitoring::Plugin -my @perf_data = sort(split(/ /, $result->perf_output)); +my $free_percent_on_mp2 = ($result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ ($absolut_space_mp2/100)); +print("free percent on mp2: ". $free_percent_on_mp2 . "\n"); +my @perfdata; +@perfdata[0] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; +@perfdata[1] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]; # Calculate avg_free free on mountpoint1 and mountpoint2 # because if you check in the middle, you should get different errors -$_ = $result->output; -my ($free_on_mp1, $free_on_mp2) = (m/\((\d+\.\d+)%.*\((\d+\.\d+)%/); -die "Cannot parse output: $_" unless ($free_on_mp1 && $free_on_mp2); -my $avg_free = ceil(($free_on_mp1+$free_on_mp2)/2); +my $avg_free_percent = ceil(($free_percent_on_mp1+$free_percent_on_mp2)/2); +# print("avg_free: " . $avg_free_percent . "\n"); my ($more_free, $less_free); -if ($free_on_mp1 > $free_on_mp2) { +if ($free_percent_on_mp1 > $free_percent_on_mp2) { $more_free = $mountpoint_valid; $less_free = $mountpoint2_valid; -} elsif ($free_on_mp1 < $free_on_mp2) { +} elsif ($free_percent_on_mp1 < $free_percent_on_mp2) { $more_free = $mountpoint2_valid; $less_free = $mountpoint_valid; } else { die "Two mountpoints are the same - cannot do rest of test"; } -if($free_on_mp1 == $avg_free || $free_on_mp2 == $avg_free) { + +print("less free: " . $less_free . "\n"); +print("more free: " . $more_free . "\n"); + +if($free_percent_on_mp1 == $avg_free_percent || $free_percent_on_mp2 == $avg_free_percent) { die "One mountpoints has average space free - cannot do rest of test"; } +my $free_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'}; +my $total_inodes_on_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'}; +my $free_inode_percentage_on_mp1 = $free_inodes_on_mp1 / ($total_inodes_on_mp1 / 100); -# Do same for inodes -$_ = $result->output; -my ($free_inode_on_mp1, $free_inode_on_mp2) = (m/inode=(\d+)%.*inode=(\d+)%/); -die "Cannot parse free inodes: $_" unless ($free_inode_on_mp1 && $free_inode_on_mp2); -my $avg_inode_free = ceil(($free_inode_on_mp1 + $free_inode_on_mp2)/2); +my $free_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'value'}->{'value'}; +my $total_inodes_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[2]->{'perfdata'}->[0]->{'max'}->{'value'}; +my $free_inode_percentage_on_mp2 = $free_inodes_on_mp2 / ($total_inodes_on_mp2 / 100); + +my $avg_inode_free_percentage = ceil(($free_inode_percentage_on_mp1 + $free_inode_percentage_on_mp2)/2); my ($more_inode_free, $less_inode_free); -if ($free_inode_on_mp1 > $free_inode_on_mp2) { +if ($free_inode_percentage_on_mp1 > $free_inode_percentage_on_mp2) { $more_inode_free = $mountpoint_valid; $less_inode_free = $mountpoint2_valid; -} elsif ($free_inode_on_mp1 < $free_inode_on_mp2) { +} elsif ($free_inode_percentage_on_mp1 < $free_inode_percentage_on_mp2) { $more_inode_free = $mountpoint2_valid; $less_inode_free = $mountpoint_valid; } else { die "Two mountpoints with same inodes free - cannot do rest of test"; } -if($free_inode_on_mp1 == $avg_inode_free || $free_inode_on_mp2 == $avg_inode_free) { +if($free_inode_percentage_on_mp1 == $avg_inode_free_percentage || $free_inode_percentage_on_mp2 == $avg_inode_free_percentage) { die "One mountpoints has average inodes free - cannot do rest of test"; } # Verify performance data # First check absolute thresholds... $result = NPTest->testCmd( - "./check_disk -w 20 -c 10 -p $mountpoint_valid" + "./check_disk -w 20 -c 10 -p $mountpoint_valid $output_format" ); -$_ = $result->perf_output; -my ($warn_absth_data, $crit_absth_data, $total_absth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); -# default unit is MiB, but perfdata is always bytes -is ($warn_absth_data, $total_absth_data - (20 * (2 ** 20)), "Wrong warning in perf data using absolute thresholds"); -is ($crit_absth_data, $total_absth_data - (10 * (2 ** 20)), "Wrong critical in perf data using absolute thresholds"); + +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); + +my $warn_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'}; +my $crit_absth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'}; +my $total_absth_data= $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'}; + +# print("warn: " .$warn_absth_data . "\n"); +# print("crit: " .$crit_absth_data . "\n"); +# print("total: " .$total_absth_data . "\n"); + +is ($warn_absth_data <=> (20 * (2 ** 20)), 0, "Wrong warning in perf data using absolute thresholds"); +is ($crit_absth_data <=> (10 * (2 ** 20)), 0, "Wrong critical in perf data using absolute thresholds"); # Then check percent thresholds. $result = NPTest->testCmd( - "./check_disk -w 20% -c 10% -p $mountpoint_valid" + "./check_disk -w 20% -c 10% -p $mountpoint_valid $output_format" ); -$_ = $result->perf_output; -my ($warn_percth_data, $crit_percth_data, $total_percth_data) = (m/=.[^;]*;(\d+);(\d+);\d+;(\d+)/); -is ($warn_percth_data, int((1-20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds"); -is ($crit_percth_data, int((1-10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds"); + +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); + +my $warn_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'warn'}->{'end'}->{'value'}; +my $crit_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'}; +my $total_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'}; + +is ($warn_percth_data <=> int((20/100)*$total_percth_data), 0, "Wrong warning in perf data using percent thresholds"); +is ($crit_percth_data <=> int((10/100)*$total_percth_data), 0, "Wrong critical in perf data using percent thresholds"); # Check when order of mount points are reversed, that perf data remains same $result = NPTest->testCmd( - "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid" + "./check_disk -w 1% -c 1% -p $mountpoint2_valid -w 1% -c 1% -p $mountpoint_valid $output_format" ); -@_ = sort(split(/ /, $result->perf_output)); -is_deeply( \@perf_data, \@_, "perf data for both filesystems same when reversed"); +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); +# write comparison set for perfdata here, but in reversed order, maybe there is a smarter way +my @perfdata2; +@perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; +@perfdata2[0] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]; +is_deeply(\@perfdata, \@perfdata2, "perf data for both filesystems same when reversed"); # Basic filesystem checks for sizes -$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free" ); -cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free"); -like ( $result->output, $successOutput, "OK output" ); -like ( $result->only_output, qr/free space/, "Have free space text"); -like ( $result->only_output, qr/$more_free/, "Have disk name in text"); +$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free $output_format"); +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); +like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free"); -$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free" ); -cmp_ok( $result->return_code, '==', 0, "At least 1 MB available on $more_free and $less_free"); +$result = NPTest->testCmd( "./check_disk -w 1 -c 1 -p $more_free -p $less_free $output_format" ); +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); +like($result->{'mp_test_result'}->{'state'}, "/OK/", "At least 1 MB available on $more_free and $less_free"); -$_ = $result->output; - -my ($free_mb_on_mp1, $free_mb_on_mp2) = (m/(\d+)MiB .* (\d+)MiB /g); +my $free_mb_on_mp1 =$result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / (1024 * 1024); +my $free_mb_on_mp2 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ (1024 * 1024); die "Cannot parse output: $_" unless ($free_mb_on_mp1 && $free_mb_on_mp2); my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2; - -$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free" ); -is( $result->only_output, "DISK OK", "No print out of disks with -e for OKs"); +$result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free $output_format" ); +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); $result = NPTest->testCmd( "./check_disk 100 100 $more_free" ); cmp_ok( $result->return_code, '==', 0, "Old syntax okay" ); @@ -139,54 +171,55 @@ $result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" ); cmp_ok( $result->return_code, "==", 0, "At least 1% free" ); $result = NPTest->testCmd( - "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free" + "./check_disk -w 1% -c 1% -p $more_free -w 100% -c 100% -p $less_free $output_format" ); -cmp_ok( $result->return_code, "==", 2, "Get critical on less_free mountpoint $less_free" ); -like( $result->output, $failureOutput, "Right output" ); +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); +like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Get critical on less_free mountpoint $less_free"); $result = NPTest->testCmd( - "./check_disk -w $avg_free% -c 0% -p $less_free" + "./check_disk -w $avg_free_percent% -c 0% -p $less_free $output_format" ); -cmp_ok( $result->return_code, '==', 1, "Get warning on less_free mountpoint, when checking avg_free"); +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); +like($result->{'mp_test_result'}->{'state'}, "/WARNING/", "Get warning on less_free mountpoint, when checking avg_free"); $result = NPTest->testCmd( - "./check_disk -w $avg_free% -c $avg_free% -p $more_free" + "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $more_free" ); cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, when checking avg_free"); $result = NPTest->testCmd( - "./check_disk -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" + "./check_disk -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free" ); cmp_ok( $result->return_code, "==", 1, "Combining above two tests, get warning"); my $all_disks = $result->output; $result = NPTest->testCmd( - "./check_disk -e -w $avg_free% -c 0% -p $less_free -w $avg_free% -c $avg_free% -p $more_free" + "./check_disk -e -w $avg_free_percent% -c 0% -p $less_free -w $avg_free_percent% -c $avg_free_percent% -p $more_free" ); isnt( $result->output, $all_disks, "-e gives different output"); # Need spaces around filesystem name in case less_free and more_free are nested like( $result->output, qr/ $less_free /, "Found problem $less_free"); unlike( $result->only_output, qr/ $more_free /, "Has ignored $more_free as not a problem"); -like( $result->perf_output, qr/ $more_free=/, "But $more_free is still in perf data"); +like( $result->perf_output, qr/'$more_free'=/, "But $more_free is still in perf data"); $result = NPTest->testCmd( - "./check_disk -w $avg_free% -c 0% -p $more_free" + "./check_disk -w $avg_free_percent% -c 0% -p $more_free" ); cmp_ok( $result->return_code, '==', 0, "Get ok on more_free mountpoint, checking avg_free"); $result = NPTest->testCmd( - "./check_disk -w $avg_free% -c $avg_free% -p $less_free" + "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free" ); cmp_ok( $result->return_code, '==', 2, "Get critical on less_free, checking avg_free"); $result = NPTest->testCmd( - "./check_disk -w $avg_free% -c 0% -p $more_free -w $avg_free% -c $avg_free% -p $less_free" + "./check_disk -w $avg_free_percent% -c 0% -p $more_free -w $avg_free_percent% -c $avg_free_percent% -p $less_free" ); cmp_ok( $result->return_code, '==', 2, "Combining above two tests, get critical"); $result = NPTest->testCmd( - "./check_disk -w $avg_free% -c $avg_free% -p $less_free -w $avg_free% -c 0% -p $more_free" + "./check_disk -w $avg_free_percent% -c $avg_free_percent% -p $less_free -w $avg_free_percent% -c 0% -p $more_free" ); cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); @@ -203,32 +236,32 @@ is( $result->return_code, 2, "Critical requesting 100% free inodes for both moun $result = NPTest->testCmd( "./check_disk --iwarning 1% --icritical 1% -p $more_inode_free -K 100% -W 100% -p $less_inode_free" ); is( $result->return_code, 2, "Get critical on less_inode_free mountpoint $less_inode_free"); -$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free" ); +$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free" ); is( $result->return_code, 1, "Get warning on less_inode_free, when checking average"); -$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free "); +$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free "); is( $result->return_code, 0, "Get ok on more_inode_free when checking average"); -$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); +$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" ); is ($result->return_code, 1, "Combine above two tests, get warning"); $all_disks = $result->output; -$result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free% -K 0% -p $less_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $more_inode_free" ); +$result = NPTest->testCmd( "./check_disk -e -W $avg_inode_free_percentage% -K 0% -p $less_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $more_inode_free" ); isnt( $result->output, $all_disks, "-e gives different output"); like( $result->output, qr/$less_inode_free/, "Found problem $less_inode_free"); unlike( $result->only_output, qr/$more_inode_free\s/, "Has ignored $more_inode_free as not a problem"); like( $result->perf_output, qr/$more_inode_free/, "But $more_inode_free is still in perf data"); -$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free" ); +$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" ); is( $result->return_code, 0, "Get ok on more_inode_free mountpoint, checking average"); -$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); +$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" ); is( $result->return_code, 2, "Get critical on less_inode_free, checking average"); -$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K 0% -p $more_inode_free -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free" ); +$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K 0% -p $more_inode_free -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free" ); is( $result->return_code, 2, "Combining above two tests, get critical"); -$result = NPTest->testCmd( "./check_disk -W $avg_inode_free% -K $avg_inode_free% -p $less_inode_free -W $avg_inode_free% -K 0% -p $more_inode_free" ); +$result = NPTest->testCmd( "./check_disk -W $avg_inode_free_percentage% -K $avg_inode_free_percentage% -p $less_inode_free -W $avg_inode_free_percentage% -K 0% -p $more_inode_free" ); cmp_ok( $result->return_code, '==', 2, "And reversing arguments should not make a difference"); @@ -249,9 +282,9 @@ $result = NPTest->testCmd( ); cmp_ok( $result->return_code, "==", 3, "Invalid options: -p must come after thresholds" ); -$result = NPTest->testCmd( "./check_disk -w 100% -c 100% ".${mountpoint_valid} ); # 100% empty -cmp_ok( $result->return_code, "==", 2, "100% empty" ); -like( $result->output, $failureOutput, "Right output" ); +$result = NPTest->testCmd( "./check_disk -w 100% -c 100% $output_format ".${mountpoint_valid} ); # 100% empty +cmp_ok( $result->return_code, "==", 0, "100% empty" ); +like($result->{'mp_test_result'}->{'state'}, "/CRITICAL/", "100% empty"); $result = NPTest->testCmd( "./check_disk -w 100000000 -c 100000000 $mountpoint_valid" ); cmp_ok( $result->return_code, '==', 2, "Check for 100TB free" ); @@ -263,7 +296,8 @@ cmp_ok( $result->return_code, "==", 2, "100 TB empty" ); # Checking old syntax of check_disk warn crit [fs], with warn/crit at USED% thresholds $result = NPTest->testCmd( "./check_disk 0 0 ".${mountpoint_valid} ); cmp_ok( $result->return_code, "==", 2, "Old syntax: 0% used"); -like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments"); +# like ( $result->only_output, qr(^[^;]*;[^;]*$), "Select only one path with positional arguments"); +# TODO not sure what the above should test, taking it out $result = NPTest->testCmd( "./check_disk 100 100 $mountpoint_valid" ); cmp_ok( $result->return_code, '==', 0, "Old syntax: 100% used" ); @@ -311,8 +345,9 @@ $result = NPTest->testCmd( "./check_disk -w 0% -c 0% -p / -p /" ); unlike( $result->output, '/ \/ .* \/ /', "Should not show same filesystem twice"); # are partitions added if -C is given without path selection -p ? -$result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid" ); -like( $result->output, '/;.*;\|/', "-C selects partitions if -p is not given"); +$result = NPTest->testCmd( "./check_disk -w 0% -c 0% -C -w 0% -c 0% -p $mountpoint_valid $output_format" ); +cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); +cmp_ok(scalar $result->{'mp_test_result'}->{'checks'}, '>', 1, "-C invokes matchall logic again"); # grouping: exit crit if the sum of free megs on mp1+mp2 is less than warn/crit $result = NPTest->testCmd( "./check_disk -w ". ($free_mb_on_all + 1) ." -c ". ($free_mb_on_all + 1) ." -g group -p $mountpoint_valid -p $mountpoint2_valid" ); @@ -359,39 +394,37 @@ like( $result->output, qr/$mountpoint2_valid/,"ignore: output data does have $mo # ignore-missing: exit okay, when fs is not accessible $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p /bob"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for not existing filesystem /bob"); -like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /bob;.*$/', 'Output OK'); +like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); # ignore-missing: exit okay, when regex does not match $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r /bob"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); -like( $result->output, '/^DISK OK - No disks were found for provided parameters.*$/', 'Output OK'); +like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); # ignore-missing: exit okay, when fs with exact match (-E) is not found $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -E -p /etc"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay when exact match does not find fs"); -like( $result->output, '/^DISK OK - No disks were found for provided parameters - ignored paths: /etc;.*$/', 'Output OK'); +like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); # ignore-missing: exit okay, when checking one existing fs and one non-existing fs (regex) $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -r '/bob' -r '^/\$'"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); -like( $result->output, '/^DISK OK - free space: \/ .*$/', 'Output OK'); # ignore-missing: exit okay, when checking one existing fs and one non-existing fs (path) $result = NPTest->testCmd( "./check_disk --ignore-missing -w 0% -c 0% -p '/bob' -p '/'"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); -like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK'); +# like( $result->output, '/^DISK OK - free space: / .*; - ignored paths: /bob;.*$/', 'Output OK'); # ignore-missing: exit okay, when checking one non-existing fs (path) and one ignored $result = NPTest->testCmd( "./check_disk -n -w 0% -c 0% -r /dummy -i /dummy2"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); -like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); +like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); # ignore-missing: exit okay, when regex match does not find anything $result = NPTest->testCmd( "./check_disk -n -e -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); -like( $result->output, '/^DISK OK\|$/', 'Output OK'); # ignore-missing: exit okay, when regex match does not find anything $result = NPTest->testCmd( "./check_disk -n -l -w 10% -c 5% -W 10% -K 5% -r /dummy"); cmp_ok( $result->return_code, '==', 0, "ignore-missing: return okay for regular expression not matching"); -like( $result->output, '/^DISK OK - No disks were found for provided parameters\|$/', 'Output OK'); +like( $result->output, '/No filesystems were found for the provided parameters.*$/', 'Output OK'); diff --git a/plugins/tests/test_check_disk.c b/plugins/tests/test_check_disk.c index 963a9413..35c57bce 100644 --- a/plugins/tests/test_check_disk.c +++ b/plugins/tests/test_check_disk.c @@ -24,7 +24,7 @@ void np_test_mount_entry_regex(struct mount_entry *dummy_mount_list, char *regstr, int cflags, int expect, char *desc); int main(int argc, char **argv) { - plan_tests(33); + plan_tests(35); struct name_list *exclude_filesystem = NULL; ok(np_find_name(exclude_filesystem, "/var/log") == false, "/var/log not in list"); @@ -81,15 +81,16 @@ int main(int argc, char **argv) { np_test_mount_entry_regex(dummy_mount_list, strdup("(/home)|(/var)"), cflags, 2, strdup("grouped regex pathname match:")); np_test_mount_entry_regex(dummy_mount_list, strdup("(/homE)|(/Var)"), cflags | REG_ICASE, 2, strdup("grouped regi pathname match:")); - struct parameter_list *paths = NULL; - np_add_parameter(&paths, "/home/groups"); - np_add_parameter(&paths, "/var"); - np_add_parameter(&paths, "/tmp"); - np_add_parameter(&paths, "/home/tonvoon"); - np_add_parameter(&paths, "/dev/c2t0d0s0"); + filesystem_list test_paths = filesystem_list_init(); + mp_int_fs_list_append(&test_paths, "/home/groups"); + mp_int_fs_list_append(&test_paths, "/var"); + mp_int_fs_list_append(&test_paths, "/tmp"); + mp_int_fs_list_append(&test_paths, "/home/tonvoon"); + mp_int_fs_list_append(&test_paths, "/dev/c2t0d0s0"); + ok(test_paths.length == 5, "List counter works correctly with appends"); - np_set_best_match(paths, dummy_mount_list, false); - for (struct parameter_list *p = paths; p; p = p->name_next) { + mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, false); + for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { struct mount_entry *temp_me; temp_me = p->best_match; if (!strcmp(p->name, "/home/groups")) { @@ -105,15 +106,19 @@ int main(int argc, char **argv) { } } - paths = NULL; /* Bad boy - should free, but this is a test suite */ - np_add_parameter(&paths, "/home/groups"); - np_add_parameter(&paths, "/var"); - np_add_parameter(&paths, "/tmp"); - np_add_parameter(&paths, "/home/tonvoon"); - np_add_parameter(&paths, "/home"); + for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { + mp_int_fs_list_del(&test_paths, p); + } + ok(test_paths.length == 0, "List delete sets counter properly"); + + mp_int_fs_list_append(&test_paths, "/home/groups"); + mp_int_fs_list_append(&test_paths, "/var"); + mp_int_fs_list_append(&test_paths, "/tmp"); + mp_int_fs_list_append(&test_paths, "/home/tonvoon"); + mp_int_fs_list_append(&test_paths, "/home"); - np_set_best_match(paths, dummy_mount_list, true); - for (struct parameter_list *p = paths; p; p = p->name_next) { + mp_int_fs_list_set_best_match(test_paths, dummy_mount_list, true); + for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { if (!strcmp(p->name, "/home/groups")) { ok(!p->best_match, "/home/groups correctly not found"); } else if (!strcmp(p->name, "/var")) { @@ -129,8 +134,8 @@ int main(int argc, char **argv) { bool found = false; /* test deleting first element in paths */ - paths = np_del_parameter(paths, NULL); - for (struct parameter_list *p = paths; p; p = p->name_next) { + mp_int_fs_list_del(&test_paths, NULL); + for (parameter_list_elem *p = test_paths.first; p; p = mp_int_fs_list_get_next(p)) { if (!strcmp(p->name, "/home/groups")) { found = true; } @@ -138,23 +143,21 @@ int main(int argc, char **argv) { ok(!found, "first element successfully deleted"); found = false; - struct parameter_list *prev = NULL; - struct parameter_list *p = paths; - while (p) { - if (!strcmp(p->name, "/tmp")) { - p = np_del_parameter(p, prev); - } else { - prev = p; - p = p->name_next; + parameter_list_elem *prev = NULL; + parameter_list_elem *p = NULL; + for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) { + if (!strcmp(path->name, "/tmp")) { + mp_int_fs_list_del(&test_paths, path); } + p = path; } - struct parameter_list *last = NULL; - for (struct parameter_list *path = paths; path; path = path->name_next) { + parameter_list_elem *last = NULL; + for (parameter_list_elem *path = test_paths.first; path; path = mp_int_fs_list_get_next(path)) { if (!strcmp(path->name, "/tmp")) { found = true; } - if (path->name_next) { + if (path->next) { prev = path; } else { last = path; @@ -163,8 +166,8 @@ int main(int argc, char **argv) { ok(!found, "/tmp element successfully deleted"); int count = 0; - p = np_del_parameter(last, prev); - for (p = paths; p; p = p->name_next) { + mp_int_fs_list_del(&test_paths, p); + for (p = test_paths.first; p; p = p->next) { if (!strcmp(p->name, "/home")) { found = true; } -- cgit v1.2.3-74-g34f1 From c4fd34ed7966a197e596f3e766f58423fe9c5ddc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 22:46:09 +0200 Subject: Codespell fixes --- plugins/check_disk.c | 6 +++--- plugins/t/check_disk.t | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 3cab816d..ddb9ee49 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -288,7 +288,7 @@ int main(int argc, char **argv) { unit.name = strdup(filesystem->group); measurements = current = add_measurement_list(NULL, unit); } else { - // if this is the first element of a group, the name of the previos entry is different + // if this is the first element of a group, the name of the previous entry is different if (strcmp(filesystem->group, current->unit.name) != 0) { // so, this must be the first element of a group measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); @@ -310,7 +310,7 @@ int main(int argc, char **argv) { mp_add_subcheck_to_check(&overall, unit_sc); } } else { - // Aparently no machting fs found + // Apparently no machting fs found mp_subcheck none_sc = mp_subcheck_init(); xasprintf(&none_sc.output, "No filesystems were found for the provided parameters"); @@ -833,7 +833,7 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { warn_freeinodes_percent, crit_freeinodes_percent); } - // If a list of paths has not been explicitely selected, find entire + // If a list of paths has not been explicitly selected, find entire // mount list and create list of paths if (!path_selected && !result.config.path_ignored) { for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t index 16daee9a..019cc9fe 100644 --- a/plugins/t/check_disk.t +++ b/plugins/t/check_disk.t @@ -39,13 +39,13 @@ like($result->{'mp_test_result'}->{'checks'}->[0]->{'state'}, "/OK/", "First sub like($result->{'mp_test_result'}->{'checks'}->[1]->{'state'}, "/OK/", "Second sub result is OK"); my $absolut_space_mp1 = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'}; -# print("absolut space on mp1: ". $absolut_space_mp1 . "\n"); +# print("absolute space on mp1: ". $absolut_space_mp1 . "\n"); my $free_percent_on_mp1 = ($result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'} / ($absolut_space_mp1/100)); print("free percent on mp1: ". $free_percent_on_mp1 . "\n"); my $absolut_space_mp2 = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'max'}->{'value'}; -# print("absolut space on mp2: ". $absolut_space_mp2 . "\n"); +# print("absolute space on mp2: ". $absolut_space_mp2 . "\n"); my $free_percent_on_mp2 = ($result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]->{'value'}->{'value'}/ ($absolut_space_mp2/100)); print("free percent on mp2: ". $free_percent_on_mp2 . "\n"); -- cgit v1.2.3-74-g34f1 From a4cf2e79f75dce3828be21726f10c755f652f710 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 23:30:51 +0200 Subject: Remove cool, comfy c23 functionality for some dirty old hacks --- plugins/check_disk.c | 42 +++++++++++++++++++++++++++------------ plugins/check_disk.d/utils_disk.c | 2 +- plugins/check_disk.d/utils_disk.h | 38 ++++++++++++++++++----------------- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index ddb9ee49..ac3933a6 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -96,6 +96,22 @@ static void print_help(void); static int verbose = 0; +// This would not be necessary in C23!! +const byte_unit Bytes_Factor = 1; +const byte_unit KibiBytes_factor = 1024; +const byte_unit MebiBytes_factor = 1048576; +const byte_unit GibiBytes_factor = 1073741824; +const byte_unit TebiBytes_factor = 1099511627776; +const byte_unit PebiBytes_factor = 1125899906842624; +const byte_unit ExbiBytes_factor = 1152921504606846976; +const byte_unit KiloBytes_factor = 1000; +const byte_unit MegaBytes_factor = 1000000; +const byte_unit GigaBytes_factor = 1000000000; +const byte_unit TeraBytes_factor = 1000000000000; +const byte_unit PetaBytes_factor = 1000000000000000; +const byte_unit ExaBytes_factor = 1000000000000000000; + + int main(int argc, char **argv) { setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -409,7 +425,7 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { bool path_selected = false; char *group = NULL; - byte_unit unit = MebiBytes; + byte_unit unit = MebiBytes_factor; result.config.mount_list = read_file_system_list(false); @@ -494,25 +510,25 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { break; case 'u': if (!strcasecmp(optarg, "bytes")) { - unit = Bytes; + unit = Bytes_Factor; } else if (!strcmp(optarg, "KiB")) { - unit = KibiBytes; + unit = KibiBytes_factor; } else if (!strcmp(optarg, "kB")) { - unit = KiloBytes; + unit = KiloBytes_factor; } else if (!strcmp(optarg, "MiB")) { - unit = MebiBytes; + unit = MebiBytes_factor; } else if (!strcmp(optarg, "MB")) { - unit = MegaBytes; + unit = MegaBytes_factor; } else if (!strcmp(optarg, "GiB")) { - unit = GibiBytes; + unit = MegaBytes_factor; } else if (!strcmp(optarg, "GB")) { - unit = GigaBytes; + unit = MegaBytes_factor; } else if (!strcmp(optarg, "TiB")) { - unit = TebiBytes; + unit = MegaBytes_factor; } else if (!strcmp(optarg, "TB")) { - unit = TeraBytes; + unit = MegaBytes_factor; } else if (!strcmp(optarg, "PiB")) { - unit = PebiBytes; + unit = MegaBytes_factor; } else if (!strcmp(optarg, "PB")) { unit = PetaBytes; } else { @@ -520,10 +536,10 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { } break; case 'k': - unit = KibiBytes; + unit = KibiBytes_factor; break; case 'm': - unit = MebiBytes; + unit = MebiBytes_factor; break; case display_unit_index: if (!strcasecmp(optarg, "bytes")) { diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c index 3986a8a8..a78b4eae 100644 --- a/plugins/check_disk.d/utils_disk.c +++ b/plugins/check_disk.d/utils_disk.c @@ -180,7 +180,7 @@ check_disk_config check_disk_config_init() { return tmp; } -char *get_unit_string(byte_unit unit) { +char *get_unit_string(byte_unit_enum unit) { switch (unit) { case Bytes: return "Bytes"; diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h index a66453ea..1f574695 100644 --- a/plugins/check_disk.d/utils_disk.h +++ b/plugins/check_disk.d/utils_disk.h @@ -8,22 +8,24 @@ #include "regex.h" #include -typedef enum : unsigned long { - Humanized = 0, - Bytes = 1, - KibiBytes = 1024, - MebiBytes = 1024 * KibiBytes, - GibiBytes = 1024 * MebiBytes, - TebiBytes = 1024 * GibiBytes, - PebiBytes = 1024 * TebiBytes, - ExbiBytes = 1024 * PebiBytes, - KiloBytes = 1000, - MegaBytes = 1000 * KiloBytes, - GigaBytes = 1000 * MegaBytes, - TeraBytes = 1000 * GigaBytes, - PetaBytes = 1000 * TeraBytes, - ExaBytes = 1000 * PetaBytes -} byte_unit; +typedef unsigned long long byte_unit; + +typedef enum { + Humanized, + Bytes, + KibiBytes, + MebiBytes, + GibiBytes, + TebiBytes, + PebiBytes, + ExbiBytes, + KiloBytes, + MegaBytes, + GigaBytes, + TeraBytes, + PetaBytes, + ExaBytes, +} byte_unit_enum; typedef struct name_list string_list; struct name_list { @@ -120,7 +122,7 @@ typedef struct { struct mount_entry *mount_list; struct name_list *seen; - byte_unit display_unit; + byte_unit_enum display_unit; // byte_unit unit; bool output_format_is_set; @@ -149,7 +151,7 @@ measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem fil int search_parameter_list(parameter_list_elem *list, const char *name); bool np_regex_match_mount_entry(struct mount_entry *, regex_t *); -char *get_unit_string(byte_unit); +char *get_unit_string(byte_unit_enum); check_disk_config check_disk_config_init(); char *humanize_byte_value(uintmax_t value, bool use_si_units); -- cgit v1.2.3-74-g34f1 From d1d6ba67065c0b1a8a61d59522e19a94eb647c94 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 23:42:50 +0200 Subject: Add debugging to tests for CI --- plugins/t/check_disk.t | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t index 019cc9fe..1d0b9838 100644 --- a/plugins/t/check_disk.t +++ b/plugins/t/check_disk.t @@ -129,6 +129,9 @@ my $warn_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[ my $crit_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'crit'}->{'end'}->{'value'}; my $total_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}[0]->{'perfdata'}->[0]->{'max'}->{'value'}; +print("warn_percth_data: " . $warn_percth_data . "\n"); +print("crit_percth_data: " . $crit_percth_data . "\n"); + is ($warn_percth_data <=> int((20/100)*$total_percth_data), 0, "Wrong warning in perf data using percent thresholds"); is ($crit_percth_data <=> int((10/100)*$total_percth_data), 0, "Wrong critical in perf data using percent thresholds"); -- cgit v1.2.3-74-g34f1 From 430c641d9c6c3efb3fb0493b35e11dbc6ede2faa Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 30 Mar 2025 23:55:16 +0200 Subject: Try to circumvent some old compiler errors --- lib/perfdata.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/perfdata.c b/lib/perfdata.c index 1742342e..f425ffcf 100644 --- a/lib/perfdata.c +++ b/lib/perfdata.c @@ -569,9 +569,6 @@ mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value left.pd_double = (double)left.pd_uint; left.type = PD_TYPE_DOUBLE; break; - case PD_TYPE_DOUBLE: - default: - // already there } switch (right.type) { @@ -583,9 +580,6 @@ mp_perfdata_value mp_pd_value_multiply(mp_perfdata_value left, mp_perfdata_value right.pd_double = (double)right.pd_uint; right.type = PD_TYPE_DOUBLE; break; - case PD_TYPE_DOUBLE: - default: - // already there } left.pd_double *= right.pd_double; -- cgit v1.2.3-74-g34f1 From d6d394fb0e1d04bbdb9304dcedad933878846266 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 31 Mar 2025 00:10:56 +0200 Subject: Fix some typos with units --- plugins/check_disk.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index ac3933a6..e53ec87f 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -520,17 +520,17 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { } else if (!strcmp(optarg, "MB")) { unit = MegaBytes_factor; } else if (!strcmp(optarg, "GiB")) { - unit = MegaBytes_factor; + unit = GibiBytes_factor; } else if (!strcmp(optarg, "GB")) { - unit = MegaBytes_factor; + unit = GigaBytes_factor; } else if (!strcmp(optarg, "TiB")) { - unit = MegaBytes_factor; + unit = TebiBytes_factor; } else if (!strcmp(optarg, "TB")) { - unit = MegaBytes_factor; + unit = TeraBytes_factor; } else if (!strcmp(optarg, "PiB")) { - unit = MegaBytes_factor; + unit = PebiBytes_factor; } else if (!strcmp(optarg, "PB")) { - unit = PetaBytes; + unit = PetaBytes_factor; } else { die(STATE_UNKNOWN, _("unit type %s not known\n"), optarg); } -- cgit v1.2.3-74-g34f1 From 1b0085c2e7196aa77d605e8cb1863064a8e5189c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 31 Mar 2025 00:46:10 +0200 Subject: Fixes problems after a4cf2e79f75dce3828be21726f10c755f652f710 --- plugins/check_disk.c | 2 +- plugins/check_disk.d/utils_disk.c | 62 ++++++++++++++++++++++++--------------- plugins/check_disk.d/utils_disk.h | 2 +- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/plugins/check_disk.c b/plugins/check_disk.c index e53ec87f..515ddff0 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -1112,7 +1112,7 @@ mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_ get_unit_string(unit), (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit)); } else { xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)", humanize_byte_value(measurement_unit.free_bytes, false), - humanize_byte_value(measurement_unit.total_bytes, false)); + humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false)); } mp_perfdata used_space = perfdata_init(); diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c index a78b4eae..eec1282b 100644 --- a/plugins/check_disk.d/utils_disk.c +++ b/plugins/check_disk.d/utils_disk.c @@ -309,7 +309,7 @@ measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem fil #define RANDOM_STRING_LENGTH 64 -char *humanize_byte_value(uintmax_t value, bool use_si_units) { +char *humanize_byte_value(unsigned long long value, bool use_si_units) { // Idea: A reasonable output should have at most 3 orders of magnitude // before the decimal separator // 353GiB is ok, 2444 GiB should be 2.386 TiB @@ -317,36 +317,52 @@ char *humanize_byte_value(uintmax_t value, bool use_si_units) { if (result == NULL) { die(STATE_UNKNOWN, _("allocation failed")); } + const byte_unit KibiBytes_factor = 1024; + const byte_unit MebiBytes_factor = 1048576; + const byte_unit GibiBytes_factor = 1073741824; + const byte_unit TebiBytes_factor = 1099511627776; + const byte_unit PebiBytes_factor = 1125899906842624; + const byte_unit ExbiBytes_factor = 1152921504606846976; + const byte_unit KiloBytes_factor = 1000; + const byte_unit MegaBytes_factor = 1000000; + const byte_unit GigaBytes_factor = 1000000000; + const byte_unit TeraBytes_factor = 1000000000000; + const byte_unit PetaBytes_factor = 1000000000000000; + const byte_unit ExaBytes_factor = 1000000000000000000; if (use_si_units) { // SI units, powers of 10 - if (value < KiloBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju B", value); - } else if (value < MegaBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju KB", value / KiloBytes); - } else if (value < GigaBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju MB", value / MegaBytes); - } else if (value < TeraBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju GB", value / GigaBytes); - } else if (value < PetaBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju TB", value / TeraBytes); + if (value < KiloBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value); + } else if (value < MegaBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu KB", value / KiloBytes_factor); + } else if (value < GigaBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu MB", value / MegaBytes_factor); + } else if (value < TeraBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu GB", value / GigaBytes_factor); + } else if (value < PetaBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu TB", value / TeraBytes_factor); + } else if (value < ExaBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu PB", value / PetaBytes_factor); } else { - snprintf(result, RANDOM_STRING_LENGTH, "%ju PB", value / PetaBytes); + snprintf(result, RANDOM_STRING_LENGTH, "%llu EB", value / ExaBytes_factor); } } else { // IEC units, powers of 2 ^ 10 - if (value < KibiBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju B", value); - } else if (value < MebiBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju KiB", value / KibiBytes); - } else if (value < GibiBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju MiB", value / MebiBytes); - } else if (value < TebiBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju GiB", value / GibiBytes); - } else if (value < PebiBytes) { - snprintf(result, RANDOM_STRING_LENGTH, "%ju TiB", value / TebiBytes); + if (value < KibiBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu B", value); + } else if (value < MebiBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu KiB", value / KibiBytes_factor); + } else if (value < GibiBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu MiB", value / MebiBytes_factor); + } else if (value < TebiBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu GiB", value / GibiBytes_factor); + } else if (value < PebiBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu TiB", value / TebiBytes_factor); + } else if (value < ExbiBytes_factor) { + snprintf(result, RANDOM_STRING_LENGTH, "%llu PiB", value / PebiBytes_factor); } else { - snprintf(result, RANDOM_STRING_LENGTH, "%ju PiB", value / PebiBytes); + snprintf(result, RANDOM_STRING_LENGTH, "%llu EiB", value / ExbiBytes_factor); } } diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h index 1f574695..6831d1fd 100644 --- a/plugins/check_disk.d/utils_disk.h +++ b/plugins/check_disk.d/utils_disk.h @@ -154,4 +154,4 @@ bool np_regex_match_mount_entry(struct mount_entry *, regex_t *); char *get_unit_string(byte_unit_enum); check_disk_config check_disk_config_init(); -char *humanize_byte_value(uintmax_t value, bool use_si_units); +char *humanize_byte_value(unsigned long long value, bool use_si_units); -- cgit v1.2.3-74-g34f1 From 24a50b9421050b3c49dd07eddc942934f66685ca Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:18:19 +0200 Subject: check_disk: decrease precision to avoid false negatives with small measurement changes --- plugins/t/check_disk.t | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t index 1d0b9838..f07b2303 100644 --- a/plugins/t/check_disk.t +++ b/plugins/t/check_disk.t @@ -54,6 +54,10 @@ my @perfdata; @perfdata[0] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; @perfdata[1] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]; +# Decrease precision of numbers since the the fs might be modified between the two runs +$perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); +$perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); + # Calculate avg_free free on mountpoint1 and mountpoint2 # because if you check in the middle, you should get different errors my $avg_free_percent = ceil(($free_percent_on_mp1+$free_percent_on_mp2)/2); @@ -144,8 +148,11 @@ cmp_ok( $result->return_code, "==", 0, "with JSON test format result should alwa # write comparison set for perfdata here, but in reversed order, maybe there is a smarter way my @perfdata2; -@perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; @perfdata2[0] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]; +@perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; +# Decrease precision of numbers since the the fs might be modified between the two runs +$perfdata2[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); +$perfdata2[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); is_deeply(\@perfdata, \@perfdata2, "perf data for both filesystems same when reversed"); # Basic filesystem checks for sizes -- cgit v1.2.3-74-g34f1 From 13c9de8c77477e78014622f5c4ff31226aeb286d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:29:49 +0200 Subject: Try fixing some tests --- plugins/t/check_disk.t | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/plugins/t/check_disk.t b/plugins/t/check_disk.t index f07b2303..0f62fb2b 100644 --- a/plugins/t/check_disk.t +++ b/plugins/t/check_disk.t @@ -26,7 +26,7 @@ my $output_format = "--output-format mp-test-json"; if ($mountpoint_valid eq "" or $mountpoint2_valid eq "") { plan skip_all => "Need 2 mountpoints to test"; } else { - plan tests => 96; + plan tests => 97; } $result = NPTest->testCmd( @@ -56,7 +56,7 @@ my @perfdata; # Decrease precision of numbers since the the fs might be modified between the two runs $perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); -$perfdata[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); +$perfdata[1]->{'value'}->{'value'} = int($perfdata[1]->{'value'}->{'value'} / 1000000); # Calculate avg_free free on mountpoint1 and mountpoint2 # because if you check in the middle, you should get different errors @@ -136,8 +136,8 @@ my $total_percth_data = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'} print("warn_percth_data: " . $warn_percth_data . "\n"); print("crit_percth_data: " . $crit_percth_data . "\n"); -is ($warn_percth_data <=> int((20/100)*$total_percth_data), 0, "Wrong warning in perf data using percent thresholds"); -is ($crit_percth_data <=> int((10/100)*$total_percth_data), 0, "Wrong critical in perf data using percent thresholds"); +is (int($warn_percth_data), int((20/100)*$total_percth_data), "Wrong warning in perf data using percent thresholds. Got " . $warn_percth_data . " with total " . $total_percth_data); +is (int($crit_percth_data), int((10/100)*$total_percth_data), "Wrong critical in perf data using percent thresholds. Got " . $crit_percth_data . " with total " . $total_percth_data); # Check when order of mount points are reversed, that perf data remains same @@ -151,8 +151,8 @@ my @perfdata2; @perfdata2[0] = $result->{'mp_test_result'}->{'checks'}->[1]->{'checks'}->[0]->{'perfdata'}->[0]; @perfdata2[1] = $result->{'mp_test_result'}->{'checks'}->[0]->{'checks'}->[0]->{'perfdata'}->[0]; # Decrease precision of numbers since the the fs might be modified between the two runs -$perfdata2[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); -$perfdata2[0]->{'value'}->{'value'} = int($perfdata[0]->{'value'}->{'value'} / 1000000); +$perfdata2[0]->{'value'}->{'value'} = int($perfdata2[0]->{'value'}->{'value'} / 1000000); +$perfdata2[1]->{'value'}->{'value'} = int($perfdata2[1]->{'value'}->{'value'} / 1000000); is_deeply(\@perfdata, \@perfdata2, "perf data for both filesystems same when reversed"); # Basic filesystem checks for sizes @@ -174,8 +174,9 @@ my $free_mb_on_all = $free_mb_on_mp1 + $free_mb_on_mp2; $result = NPTest->testCmd( "./check_disk -e -w 1 -c 1 -p $more_free $output_format" ); cmp_ok( $result->return_code, "==", 0, "with JSON test format result should always be OK"); -$result = NPTest->testCmd( "./check_disk 100 100 $more_free" ); -cmp_ok( $result->return_code, '==', 0, "Old syntax okay" ); +$result = NPTest->testCmd( "./check_disk 101 101 $more_free" ); +like($result->output, "/OK/", "OK in Output"); +cmp_ok( $result->return_code, '==', 0, "Old syntax okay, output was: ". $result->output . "\n" ); $result = NPTest->testCmd( "./check_disk -w 1% -c 1% -p $more_free" ); cmp_ok( $result->return_code, "==", 0, "At least 1% free" ); -- cgit v1.2.3-74-g34f1 From d0647ec7e1500c0e6164ac9820a7d623582bdde2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 31 Mar 2025 23:41:51 +0200 Subject: Some code simplifications --- lib/utils_base.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/utils_base.c b/lib/utils_base.c index ff9540c7..c49a473f 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -225,27 +225,15 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { if (my_range.end_infinity == false && my_range.start_infinity == false) { // range: .........|---inside---|........... // value - if ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)) { - is_inside = true; - } else { - is_inside = false; - } + is_inside = ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)); } else if (my_range.start_infinity == false && my_range.end_infinity == true) { // range: .........|---inside--------- // value - if (cmp_perfdata_value(my_range.start, value) < 0) { - is_inside = true; - } else { - is_inside = false; - } + is_inside = (cmp_perfdata_value(my_range.start, value) < 0); } else if (my_range.start_infinity == true && my_range.end_infinity == false) { // range: -inside--------|.................... // value - if (cmp_perfdata_value(value, my_range.end) == -1) { - is_inside = true; - } else { - is_inside = false; - } + is_inside = (cmp_perfdata_value(value, my_range.end) == -1); } else { // range from -inf to inf, so always inside is_inside = true; -- cgit v1.2.3-74-g34f1 From 4ab8a54c52fa78c1d8d757bcb75a8e5d4b3e560f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 2 Apr 2025 00:13:20 +0200 Subject: check_icmp: linter fixes --- plugins-root/check_icmp.c | 131 ++++++++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 57 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index dcaceddb..6a4239a8 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -208,29 +208,25 @@ typedef enum enum_threshold_mode threshold_mode; /** prototypes **/ void print_help(void); void print_usage(void); -static u_int get_timevar(const char *); -static u_int get_timevaldiff(struct timeval *, struct timeval *); -static in_addr_t get_ip_address(const char *); -static int wait_for_reply(int, u_int); -static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *); -static int send_icmp_ping(int, struct rta_host *); +static u_int get_timevar(const char * /*str*/); +static u_int get_timevaldiff(struct timeval * /*early*/, struct timeval * /*later*/); +static in_addr_t get_ip_address(const char * /*ifname*/); +static int wait_for_reply(int /*sock*/, u_int /*t*/); +static int recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, struct sockaddr * /*saddr*/, u_int * /*timo*/, + struct timeval * /*tv*/); +static int send_icmp_ping(int /*sock*/, struct rta_host * /*host*/); static int get_threshold(char *str, threshold *th); -static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode); +static bool get_threshold2(char *str, size_t length, threshold * /*warn*/, threshold * /*crit*/, threshold_mode mode); static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); static void run_checks(void); -static void set_source_ip(char *); -static int add_target(char *); -static int add_target_ip(char *, struct sockaddr_storage *); -static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); -static void parse_address(struct sockaddr_storage *, char *, int); -static unsigned short icmp_checksum(uint16_t *, size_t); -static void finish(int); -static void crash(const char *, ...); - -/** external **/ -extern int optind; -extern char *optarg; -extern char **environ; +static void set_source_ip(char * /*arg*/); +static int add_target(char * /*arg*/); +static int add_target_ip(char * /*arg*/, struct sockaddr_storage * /*in*/); +static int handle_random_icmp(unsigned char * /*packet*/, struct sockaddr_storage * /*addr*/); +static void parse_address(struct sockaddr_storage * /*addr*/, char * /*address*/, int /*size*/); +static unsigned short icmp_checksum(uint16_t * /*p*/, size_t /*n*/); +static void finish(int /*sig*/); +static void crash(const char * /*fmt*/, ...); /** global variables **/ static struct rta_host **table, *cursor, *list; @@ -386,7 +382,8 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm } static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { - struct icmp p, sent_icmp; + struct icmp p; + struct icmp sent_icmp; struct rta_host *host = NULL; memcpy(&p, packet, sizeof(p)); @@ -469,7 +466,9 @@ int main(int argc, char **argv) { int i; char *ptr; long int arg; - int icmp_sockerrno, udp_sockerrno, tcp_sockerrno; + int icmp_sockerrno; + int udp_sockerrno; + int tcp_sockerrno; int result; struct rta_host *host; #ifdef HAVE_SIGACTION @@ -880,8 +879,10 @@ int main(int argc, char **argv) { } static void run_checks(void) { - u_int i, t; - u_int final_wait, time_passed; + u_int i; + u_int t; + u_int final_wait; + u_int time_passed; /* this loop might actually violate the pkt_interval or target_interval * settings, but only if there aren't any packets on the wire which @@ -940,15 +941,19 @@ static void run_checks(void) { * icmp echo reply : the rest */ static int wait_for_reply(int sock, u_int t) { - int n, hlen; + int n; + int hlen; static unsigned char buf[65536]; struct sockaddr_storage resp_addr; union ip_hdr *ip; union icmp_packet packet; struct rta_host *host; struct icmp_ping_data data; - struct timeval wait_start, now; - u_int tdiff, i, per_pkt_wait; + struct timeval wait_start; + struct timeval now; + u_int tdiff; + u_int i; + u_int per_pkt_wait; double jitter_tmp; if (!(packet.buf = malloc(icmp_pkt_size))) { @@ -1045,14 +1050,14 @@ static int wait_for_reply(int sock, u_int t) { if (address_family == PF_INET) { memcpy(&data, packet.icp->icmp_data, sizeof(data)); if (debug > 2) { - printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), + printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data), ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); } host = table[ntohs(packet.icp->icmp_seq) / packets]; } else { memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); if (debug > 2) { - printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id), + printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data), ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); } host = table[ntohs(packet.icp6->icmp6_seq) / packets]; @@ -1182,8 +1187,8 @@ static int send_icmp_ping(int sock, struct rta_host *host) { icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); if (debug > 2) { - printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), - ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", sizeof(data), ntohs(icp->icmp_id), + ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); } } else { struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; @@ -1199,8 +1204,8 @@ static int send_icmp_ping(int sock, struct rta_host *host) { // let checksum be calculated automatically if (debug > 2) { - printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), - ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", sizeof(data), ntohs(icp6->icmp6_id), + ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); } } @@ -1228,7 +1233,7 @@ static int send_icmp_ping(int sock, struct rta_host *host) { if (len < 0 || (unsigned int)len != icmp_pkt_size) { if (debug) { char address[INET6_ADDRSTRLEN]; - parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address)); + parse_address((&host->saddr_in), address, sizeof(address)); printf("Failed to send ping to %s: %s\n", address, strerror(errno)); } errno = 0; @@ -1243,9 +1248,13 @@ static int send_icmp_ping(int sock, struct rta_host *host) { static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { u_int slen; - int n, ret; - struct timeval to, then, now; - fd_set rd, wr; + int n; + int ret; + struct timeval to; + struct timeval then; + struct timeval now; + fd_set rd; + fd_set wr; #ifdef HAVE_MSGHDR_MSG_CONTROL char ans_data[4096]; #endif // HAVE_MSGHDR_MSG_CONTROL @@ -1701,8 +1710,10 @@ static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { static int add_target_ip(char *arg, struct sockaddr_storage *in) { struct rta_host *host; - struct sockaddr_in *sin, *host_sin; - struct sockaddr_in6 *sin6, *host_sin6; + struct sockaddr_in *sin; + struct sockaddr_in *host_sin; + struct sockaddr_in6 *sin6; + struct sockaddr_in6 *host_sin6; if (address_family == AF_INET) { sin = (struct sockaddr_in *)in; @@ -1786,9 +1797,12 @@ static int add_target_ip(char *arg, struct sockaddr_storage *in) { /* wrapper for add_target_ip */ static int add_target(char *arg) { - int error, result = -1; + int error; + int result = -1; struct sockaddr_storage ip; - struct addrinfo hints, *res, *p; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *p; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; @@ -1826,22 +1840,21 @@ static int add_target(char *arg) { if (result == 1) { /* don't add all ip's if we were given a specific one */ return add_target_ip(arg, &ip); + } + errno = 0; + memset(&hints, 0, sizeof(hints)); + if (address_family == -1) { + hints.ai_family = AF_UNSPEC; } else { + hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6; + } + hints.ai_socktype = SOCK_RAW; + if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { errno = 0; - memset(&hints, 0, sizeof(hints)); - if (address_family == -1) { - hints.ai_family = AF_UNSPEC; - } else { - hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6; - } - hints.ai_socktype = SOCK_RAW; - if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { - errno = 0; - crash("Failed to resolve %s: %s", arg, gai_strerror(error)); - return -1; - } - address_family = res->ai_family; + crash("Failed to resolve %s: %s", arg, gai_strerror(error)); + return -1; } + address_family = res->ai_family; /* possibly add all the IP's as targets */ for (p = res; p != NULL; p = p->ai_next) { @@ -1907,9 +1920,12 @@ static in_addr_t get_ip_address(const char *ifname) { * return value is in microseconds */ static u_int get_timevar(const char *str) { - char p, u, *ptr; + char p; + char u; + char *ptr; size_t len; - u_int i, d; /* integer and decimal, respectively */ + u_int i; + u_int d; /* integer and decimal, respectively */ u_int factor = 1000; /* default to milliseconds */ if (!str) { @@ -1970,7 +1986,8 @@ static u_int get_timevar(const char *str) { /* not too good at checking errors, but it'll do (main() should barfe on -1) */ static int get_threshold(char *str, threshold *th) { - char *p = NULL, i = 0; + char *p = NULL; + char i = 0; if (!str || !strlen(str) || !th) { return -1; -- cgit v1.2.3-74-g34f1 From f62f182653650f274be0018d7a8f166aa99719d7 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 2 Apr 2025 01:04:25 +0200 Subject: check_icmp: localise variables --- plugins-root/check_icmp.c | 208 ++++++++++++++++++++-------------------------- 1 file changed, 89 insertions(+), 119 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 6a4239a8..061e7d82 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -65,6 +65,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include #include +#include /** sometimes undefined system macros (quite a few, actually) **/ #ifndef MAXTTL @@ -261,10 +262,10 @@ static bool order_mode = false; /** code start **/ static void crash(const char *fmt, ...) { - va_list ap; printf("%s: ", progname); + va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); @@ -383,9 +384,6 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { struct icmp p; - struct icmp sent_icmp; - struct rta_host *host = NULL; - memcpy(&p, packet, sizeof(p)); if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { /* echo request from us to us (pinging localhost) */ @@ -412,6 +410,7 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad /* might be for us. At least it holds the original package (according * to RFC 792). If it isn't, just ignore it */ + struct icmp sent_icmp; memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) { if (debug) { @@ -421,7 +420,7 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad } /* it is indeed a response for us */ - host = table[ntohs(sent_icmp.icmp_seq) / packets]; + struct rta_host *host = table[ntohs(sent_icmp.icmp_seq) / packets]; if (debug) { char address[INET6_ADDRSTRLEN]; parse_address(addr, address, sizeof(address)); @@ -463,28 +462,15 @@ void parse_address(struct sockaddr_storage *addr, char *address, int size) { } int main(int argc, char **argv) { - int i; - char *ptr; - long int arg; - int icmp_sockerrno; - int udp_sockerrno; - int tcp_sockerrno; - int result; - struct rta_host *host; -#ifdef HAVE_SIGACTION - struct sigaction sig_action; -#endif -#ifdef SO_TIMESTAMP - int on = 1; -#endif - char *source_ip = NULL; - char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64"; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); /* we only need to be setsuid when we get the sockets, so do * that before pointer magic (esp. on network data) */ + int icmp_sockerrno; + int udp_sockerrno; + int tcp_sockerrno; icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0; address_family = -1; @@ -492,7 +478,7 @@ int main(int argc, char **argv) { /* get calling name the old-fashioned way for portability instead * of relying on the glibc-ism __progname */ - ptr = strrchr(argv[0], '/'); + char *ptr = strrchr(argv[0], '/'); if (ptr) { progname = &ptr[1]; } else { @@ -549,7 +535,9 @@ int main(int argc, char **argv) { } /* Parse protocol arguments first */ - for (i = 1; i < argc; i++) { + char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64"; + for (int i = 1; i < argc; i++) { + long int arg; while ((arg = getopt(argc, argv, opts_str)) != EOF) { switch (arg) { case '4': @@ -578,7 +566,9 @@ int main(int argc, char **argv) { unsigned long size; bool err; /* parse the arguments */ - for (i = 1; i < argc; i++) { + char *source_ip = NULL; + for (int i = 1; i < argc; i++) { + long int arg; while ((arg = getopt(argc, argv, opts_str)) != EOF) { switch (arg) { case 'v': @@ -732,6 +722,7 @@ int main(int argc, char **argv) { } #ifdef SO_TIMESTAMP + int on = 1; if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) { if (debug) { printf("Warning: no SO_TIMESTAMP support\n"); @@ -767,7 +758,7 @@ int main(int argc, char **argv) { } if (icmp_sock) { - result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl)); + int result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl)); if (debug) { if (result == -1) { printf("setsockopt failed\n"); @@ -801,6 +792,7 @@ int main(int argc, char **argv) { } #ifdef HAVE_SIGACTION + struct sigaction sig_action; sig_action.sa_sigaction = NULL; sig_action.sa_handler = finish; sigfillset(&sig_action.sa_mask); @@ -856,13 +848,13 @@ int main(int argc, char **argv) { crash("minimum alive hosts is negative (%i)", min_hosts_alive); } - host = list; + struct rta_host *host = list; table = malloc(sizeof(struct rta_host *) * targets); if (!table) { crash("main(): malloc failed for host table"); } - i = 0; + int i = 0; while (host) { host->id = i * packets; table[i] = host; @@ -879,16 +871,11 @@ int main(int argc, char **argv) { } static void run_checks(void) { - u_int i; - u_int t; - u_int final_wait; - u_int time_passed; - /* this loop might actually violate the pkt_interval or target_interval * settings, but only if there aren't any packets on the wire which * indicates that the target can handle an increased packet rate */ - for (i = 0; i < packets; i++) { - for (t = 0; t < targets; t++) { + for (u_int i = 0; i < packets; i++) { + for (u_int t = 0; t < targets; t++) { /* don't send useless packets */ if (!targets_alive) { finish(0); @@ -908,8 +895,8 @@ static void run_checks(void) { } if (icmp_pkts_en_route && targets_alive) { - time_passed = get_timevaldiff(NULL, NULL); - final_wait = max_completion_time - time_passed; + u_int time_passed = get_timevaldiff(NULL, NULL); + u_int final_wait = max_completion_time - time_passed; if (debug) { printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time); @@ -941,21 +928,7 @@ static void run_checks(void) { * icmp echo reply : the rest */ static int wait_for_reply(int sock, u_int t) { - int n; - int hlen; - static unsigned char buf[65536]; - struct sockaddr_storage resp_addr; - union ip_hdr *ip; union icmp_packet packet; - struct rta_host *host; - struct icmp_ping_data data; - struct timeval wait_start; - struct timeval now; - u_int tdiff; - u_int i; - u_int per_pkt_wait; - double jitter_tmp; - if (!(packet.buf = malloc(icmp_pkt_size))) { crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); return -1; /* might be reached if we're in debug mode */ @@ -969,10 +942,15 @@ static int wait_for_reply(int sock, u_int t) { return 0; } + struct timeval wait_start; gettimeofday(&wait_start, &tz); - i = t; - per_pkt_wait = t / icmp_pkts_en_route; + u_int i = t; + struct sockaddr_storage resp_addr; + u_int per_pkt_wait = t / icmp_pkts_en_route; + static unsigned char buf[65536]; + union ip_hdr *ip; + struct timeval now; while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) { t = per_pkt_wait; @@ -982,7 +960,7 @@ static int wait_for_reply(int sock, u_int t) { } /* reap responses until we hit a timeout */ - n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); + int n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); if (!n) { if (debug > 1) { printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); @@ -1015,7 +993,7 @@ static int wait_for_reply(int sock, u_int t) { * off the bottom 4 bits */ /* hlen = (ip->ip_vhl & 0x0f) << 2; */ /* #else */ - hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2; + int hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2; /* #endif */ if (n < (hlen + ICMP_MINLEN)) { @@ -1047,6 +1025,8 @@ static int wait_for_reply(int sock, u_int t) { } /* this is indeed a valid response */ + struct rta_host *host; + struct icmp_ping_data data; if (address_family == PF_INET) { memcpy(&data, packet.icp->icmp_data, sizeof(data)); if (debug > 2) { @@ -1063,10 +1043,11 @@ static int wait_for_reply(int sock, u_int t) { host = table[ntohs(packet.icp6->icmp6_seq) / packets]; } - tdiff = get_timevaldiff(&data.stime, &now); + u_int tdiff = get_timevaldiff(&data.stime, &now); if (host->last_tdiff > 0) { /* Calculate jitter */ + double jitter_tmp; if (host->last_tdiff > tdiff) { jitter_tmp = host->last_tdiff - tdiff; } else { @@ -1143,20 +1124,14 @@ static int wait_for_reply(int sock, u_int t) { /* the ping functions */ static int send_icmp_ping(int sock, struct rta_host *host) { - long int len; - size_t addrlen; - struct icmp_ping_data data; - struct msghdr hdr; - struct iovec iov; - struct timeval tv; - void *buf = NULL; - if (sock == -1) { errno = 0; crash("Attempt to send on bogus socket"); return -1; } + void *buf = NULL; + if (!buf) { if (!(buf = malloc(icmp_pkt_size))) { crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); @@ -1165,14 +1140,18 @@ static int send_icmp_ping(int sock, struct rta_host *host) { } memset(buf, 0, icmp_pkt_size); + struct timeval tv; if ((gettimeofday(&tv, &tz)) == -1) { free(buf); return -1; } + struct icmp_ping_data data; data.ping_id = 10; /* host->icmp.icmp_sent; */ memcpy(&data.stime, &tv, sizeof(tv)); + size_t addrlen; + if (address_family == AF_INET) { struct icmp *icp = (struct icmp *)buf; addrlen = sizeof(struct sockaddr_in); @@ -1209,10 +1188,12 @@ static int send_icmp_ping(int sock, struct rta_host *host) { } } + struct iovec iov; memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = icmp_pkt_size; + struct msghdr hdr; memset(&hdr, 0, sizeof(hdr)); hdr.msg_name = (struct sockaddr *)&host->saddr_in; hdr.msg_namelen = addrlen; @@ -1221,6 +1202,7 @@ static int send_icmp_ping(int sock, struct rta_host *host) { errno = 0; + long int len; /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ #ifdef MSG_CONFIRM len = sendmsg(sock, &hdr, MSG_CONFIRM); @@ -1247,19 +1229,9 @@ static int send_icmp_ping(int sock, struct rta_host *host) { } static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { - u_int slen; - int n; - int ret; - struct timeval to; - struct timeval then; - struct timeval now; - fd_set rd; - fd_set wr; #ifdef HAVE_MSGHDR_MSG_CONTROL char ans_data[4096]; #endif // HAVE_MSGHDR_MSG_CONTROL - struct msghdr hdr; - struct iovec iov; #ifdef SO_TIMESTAMP struct cmsghdr *chdr; #endif @@ -1271,18 +1243,24 @@ static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr * return 0; } + struct timeval to; to.tv_sec = *timo / 1000000; to.tv_usec = (*timo - (to.tv_sec * 1000000)); + fd_set rd; + fd_set wr; FD_ZERO(&rd); FD_ZERO(&wr); FD_SET(sock, &rd); errno = 0; + + struct timeval then; gettimeofday(&then, &tz); - n = select(sock + 1, &rd, &wr, NULL, &to); + int n = select(sock + 1, &rd, &wr, NULL, &to); if (n < 0) { crash("select() in recvfrom_wto"); } + struct timeval now; gettimeofday(&now, &tz); *timo = get_timevaldiff(&then, &now); @@ -1290,12 +1268,14 @@ static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr * return 0; /* timeout */ } - slen = sizeof(struct sockaddr_storage); + u_int slen = sizeof(struct sockaddr_storage); + struct iovec iov; memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; iov.iov_len = len; + struct msghdr hdr; memset(&hdr, 0, sizeof(hdr)); hdr.msg_name = saddr; hdr.msg_namelen = slen; @@ -1306,7 +1286,7 @@ static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr * hdr.msg_controllen = sizeof(ans_data); #endif - ret = recvmsg(sock, &hdr, 0); + int ret = recvmsg(sock, &hdr, 0); #ifdef SO_TIMESTAMP for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { @@ -1322,16 +1302,6 @@ static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr * } static void finish(int sig) { - u_int i = 0; - unsigned char pl; - double rta; - struct rta_host *host; - const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"}; - int hosts_ok = 0; - int hosts_warn = 0; - int this_status; - double R; - alarm(0); if (debug > 1) { printf("finish(%d) called\n", sig); @@ -1354,11 +1324,17 @@ static void finish(int sig) { /* iterate thrice to calculate values, give output, and print perfparse */ status = STATE_OK; - host = list; + struct rta_host *host = list; + u_int i = 0; + const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"}; + int hosts_ok = 0; + int hosts_warn = 0; while (host) { - this_status = STATE_OK; + int this_status = STATE_OK; + unsigned char pl; + double rta; if (!host->icmp_recv) { /* rta 0 is ofcourse not entirely correct, but will still show up * conspicuously as missing entries in perfparse and cacti */ @@ -1398,6 +1374,7 @@ static void finish(int sig) { */ host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10; + double R; if (host->EffectiveLatency < 160) { R = 93.2 - (host->EffectiveLatency / 40); } else { @@ -1687,7 +1664,6 @@ static void finish(int sig) { } static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { - u_int ret; struct timeval now; if (!later) { @@ -1702,19 +1678,15 @@ static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) { return 0; } - ret = (later->tv_sec - early->tv_sec) * 1000000; + u_int ret = (later->tv_sec - early->tv_sec) * 1000000; ret += later->tv_usec - early->tv_usec; return ret; } static int add_target_ip(char *arg, struct sockaddr_storage *in) { - struct rta_host *host; struct sockaddr_in *sin; - struct sockaddr_in *host_sin; struct sockaddr_in6 *sin6; - struct sockaddr_in6 *host_sin6; - if (address_family == AF_INET) { sin = (struct sockaddr_in *)in; } else { @@ -1729,7 +1701,9 @@ static int add_target_ip(char *arg, struct sockaddr_storage *in) { } /* no point in adding two identical IP's, so don't. ;) */ - host = list; + struct sockaddr_in *host_sin; + struct sockaddr_in6 *host_sin6; + struct rta_host *host = list; while (host) { host_sin = (struct sockaddr_in *)&host->saddr_in; host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; @@ -1797,14 +1771,10 @@ static int add_target_ip(char *arg, struct sockaddr_storage *in) { /* wrapper for add_target_ip */ static int add_target(char *arg) { - int error; - int result = -1; struct sockaddr_storage ip; - struct addrinfo hints; - struct addrinfo *res; - struct addrinfo *p; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; + int result = -1; switch (address_family) { case -1: @@ -1841,6 +1811,8 @@ static int add_target(char *arg) { /* don't add all ip's if we were given a specific one */ return add_target_ip(arg, &ip); } + + struct addrinfo hints; errno = 0; memset(&hints, 0, sizeof(hints)); if (address_family == -1) { @@ -1849,6 +1821,9 @@ static int add_target(char *arg) { hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6; } hints.ai_socktype = SOCK_RAW; + + int error; + struct addrinfo *res; if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { errno = 0; crash("Failed to resolve %s: %s", arg, gai_strerror(error)); @@ -1857,7 +1832,7 @@ static int add_target(char *arg) { address_family = res->ai_family; /* possibly add all the IP's as targets */ - for (p = res; p != NULL; p = p->ai_next) { + for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { memcpy(&ip, p->ai_addr, p->ai_addrlen); add_target_ip(arg, &ip); @@ -1920,26 +1895,19 @@ static in_addr_t get_ip_address(const char *ifname) { * return value is in microseconds */ static u_int get_timevar(const char *str) { - char p; - char u; - char *ptr; - size_t len; - u_int i; - u_int d; /* integer and decimal, respectively */ - u_int factor = 1000; /* default to milliseconds */ - if (!str) { return 0; } - len = strlen(str); + + size_t len = strlen(str); if (!len) { return 0; } /* unit might be given as ms|m (millisec), * us|u (microsec) or just plain s, for seconds */ - p = '\0'; - u = str[len - 1]; + char p = '\0'; + char u = str[len - 1]; if (len >= 2 && !isdigit((int)str[len - 2])) { p = str[len - 2]; } @@ -1952,6 +1920,7 @@ static u_int get_timevar(const char *str) { printf("evaluating %s, u: %c, p: %c\n", str, u, p); } + u_int factor = 1000; /* default to milliseconds */ if (u == 'u') { factor = 1; /* microseconds */ } else if (u == 'm') { @@ -1963,6 +1932,8 @@ static u_int get_timevar(const char *str) { printf("factor is %u\n", factor); } + char *ptr; + u_int i; i = strtoul(str, &ptr, 0); if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { return i * factor; @@ -1973,7 +1944,8 @@ static u_int get_timevar(const char *str) { return i; } - d = strtoul(ptr + 1, NULL, 0); + /* integer and decimal, respectively */ + u_int d = strtoul(ptr + 1, NULL, 0); /* d is decimal, so get rid of excess digits */ while (d >= factor) { @@ -1986,15 +1958,13 @@ static u_int get_timevar(const char *str) { /* not too good at checking errors, but it'll do (main() should barfe on -1) */ static int get_threshold(char *str, threshold *th) { - char *p = NULL; - char i = 0; - if (!str || !strlen(str) || !th) { return -1; } /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ - p = &str[strlen(str) - 1]; + char i = 0; + char *p = &str[strlen(str) - 1]; while (p != &str[1]) { if (*p == '%') { *p = '\0'; @@ -2098,7 +2068,6 @@ static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, thre } unsigned short icmp_checksum(uint16_t *p, size_t n) { - unsigned short cksum; long sum = 0; /* sizeof(uint16_t) == 2 */ @@ -2114,7 +2083,8 @@ unsigned short icmp_checksum(uint16_t *p, size_t n) { sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ - cksum = ~sum; /* ones-complement, trunc to 16 bits */ + unsigned short cksum; + cksum = ~sum; /* ones-complement, trunc to 16 bits */ return cksum; } -- cgit v1.2.3-74-g34f1 From 73b42dd08b11be79bbeb4a09910bbe37699ff3b9 Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Mon, 21 Apr 2025 00:44:41 +0200 Subject: CI: Install gawk on fedora > 41 --- .github/os_detect.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/os_detect.sh b/.github/os_detect.sh index ee9c145d..47c762d3 100644 --- a/.github/os_detect.sh +++ b/.github/os_detect.sh @@ -1,10 +1,17 @@ #!/bin/sh -e + +. /etc/os-release + # workaround for really bare-bones Archlinux containers: if [ -x "$(command -v pacman)" ]; then pacman --noconfirm -Sy pacman --noconfirm -S grep gawk sed fi +if [ ${ID} == "fedora" -a ${VERSION_ID} -gt 41 ]; then + dnf install -y gawk +fi + os_release_file= if [ -s "/etc/os-release" ]; then os_release_file="/etc/os-release" -- cgit v1.2.3-74-g34f1 From 14169fe5a11b94a9f9ab44336fdb5170ee98be66 Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Sat, 19 Apr 2025 14:37:46 +0200 Subject: check_http: Adding deprecation text --- plugins/check_http.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/check_http.c b/plugins/check_http.c index baff682a..8e0c15ec 100644 --- a/plugins/check_http.c +++ b/plugins/check_http.c @@ -1724,6 +1724,16 @@ print_help (void) printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); printf ("%s\n", _("certificate expiration times.")); + printf ("\n"); + printf ("%s\n", _("ATTENTION!")); + printf ("\n"); + printf ("%s\n", _("THIS PLUGIN IS DEPRECATED. The functionality was reimplemented by the")); + printf ("%s\n", _("check_curl plugin, which can be used as a drop-in replacement. You should")); + printf ("%s\n", _("migrate your checks over to check_curl, because check_http is going to be")); + printf ("%s\n", _("removed sooner than later. Just replace check_http with check_curl in your")); + printf ("%s\n", _("check command definitions.")); + printf ("%s\n", _("Report issues to: https://github.com/monitoring-plugins/monitoring-plugins/issues")); + printf ("\n\n"); print_usage (); -- cgit v1.2.3-74-g34f1 From c975182145939e0f4e1c1b35c415c3451dd83d7d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 27 Apr 2025 17:50:59 +0200 Subject: clang-format: reduce line lenght to 100 char, it's easier to read --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index ca411edd..0ff68114 100644 --- a/.clang-format +++ b/.clang-format @@ -4,7 +4,7 @@ TabWidth: 4 AllowShortIfStatementsOnASingleLine: false BreakBeforeBraces: Attach AlignConsecutiveMacros: true -ColumnLimit: 140 +ColumnLimit: 100 IndentPPDirectives: AfterHash SortIncludes: Never AllowShortEnumsOnASingleLine: false -- cgit v1.2.3-74-g34f1 From eafee9c3f91879afa82749fa1d8cd2b0b53a5d5c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:01:01 +0200 Subject: WIP: check_icmp refactor --- plugins-root/Makefile.am | 4 +- plugins-root/check_icmp.c | 1287 ++++++++++++------------ plugins-root/check_icmp.d/check_icmp_helpers.c | 142 +++ plugins-root/check_icmp.d/check_icmp_helpers.h | 73 ++ 4 files changed, 877 insertions(+), 629 deletions(-) create mode 100644 plugins-root/check_icmp.d/check_icmp_helpers.c create mode 100644 plugins-root/check_icmp.d/check_icmp_helpers.h diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am index a80229e2..f09f5e07 100644 --- a/plugins-root/Makefile.am +++ b/plugins-root/Makefile.am @@ -24,7 +24,8 @@ noinst_PROGRAMS = check_dhcp check_icmp @EXTRAS_ROOT@ EXTRA_PROGRAMS = pst3 -EXTRA_DIST = t pst3.c +EXTRA_DIST = t pst3.c \ + check_icmp.d BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) @@ -82,6 +83,7 @@ install-exec-local: $(noinst_PROGRAMS) # the actual targets check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) +check_icmp_SOURCES = check_icmp.c check_icmp.d/check_icmp_helpers.c # -m64 needed at compiler and linker phase pst3_CFLAGS = @PST3CFLAGS@ diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 061e7d82..87dac21d 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -67,6 +67,10 @@ const char *email = "devel@monitoring-plugins.org"; #include #include +#include "../lib/states.h" +#include "./check_icmp.d/config.h" +#include "./check_icmp.d/check_icmp_helpers.h" + /** sometimes undefined system macros (quite a few, actually) **/ #ifndef MAXTTL # define MAXTTL 255 @@ -97,56 +101,8 @@ const char *email = "devel@monitoring-plugins.org"; # define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 #endif -typedef unsigned short range_t; /* type for get_range() -- unimplemented */ - -typedef struct rta_host { - unsigned short id; /* id in **table, and icmp pkts */ - char *name; /* arg used for adding this host */ - char *msg; /* icmp error message, if any */ - struct sockaddr_storage saddr_in; /* the address of this host */ - struct sockaddr_storage error_addr; /* stores address of error replies */ - unsigned long long time_waited; /* total time waited, in usecs */ - unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ - unsigned char icmp_type, icmp_code; /* type and code from errors */ - unsigned short flags; /* control/status flags */ - double rta; /* measured RTA */ - int rta_status; // check result for RTA checks - double rtmax; /* max rtt */ - double rtmin; /* min rtt */ - double jitter; /* measured jitter */ - int jitter_status; // check result for Jitter checks - double jitter_max; /* jitter rtt maximum */ - double jitter_min; /* jitter rtt minimum */ - double EffectiveLatency; - double mos; /* Mean opnion score */ - int mos_status; // check result for MOS checks - double score; /* score */ - int score_status; // check result for score checks - u_int last_tdiff; - u_int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */ - unsigned char pl; /* measured packet loss */ - int pl_status; // check result for packet loss checks - struct rta_host *next; /* linked list */ - int order_status; // check result for packet order checks -} rta_host; - #define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */ -/* threshold structure. all values are maximum allowed, exclusive */ -typedef struct threshold { - unsigned char pl; /* max allowed packet loss in percent */ - unsigned int rta; /* roundtrip time average, microseconds */ - double jitter; /* jitter time average, microseconds */ - double mos; /* MOS */ - double score; /* Score */ -} threshold; - -/* the data structure */ -typedef struct icmp_ping_data { - struct timeval stime; /* timestamp (saved in protocol struct as well) */ - unsigned short ping_id; -} icmp_ping_data; - typedef union ip_hdr { struct ip ip; struct ip6_hdr ip6; @@ -159,24 +115,6 @@ typedef union icmp_packet { u_short *cksum_in; } icmp_packet; -/* the different modes of this program are as follows: - * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping) - * MODE_HOSTCHECK: Return immediately upon any sign of life - * In addition, sends packets to ALL addresses assigned - * to this host (as returned by gethostbyname() or - * gethostbyaddr() and expects one host only to be checked at - * a time. Therefore, any packet response what so ever will - * count as a sign of life, even when received outside - * crit.rta limit. Do not misspell any additional IP's. - * MODE_ALL: Requires packets from ALL requested IP to return OK (default). - * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without - * tcp and udp args does this) - */ -#define MODE_RTA 0 -#define MODE_HOSTCHECK 1 -#define MODE_ALL 2 -#define MODE_ICMP 3 - enum enum_threshold_mode { const_rta_mode, const_packet_loss_mode, @@ -187,78 +125,325 @@ enum enum_threshold_mode { typedef enum enum_threshold_mode threshold_mode; -/* the different ping types we can do - * TODO: investigate ARP ping as well */ -#define HAVE_ICMP 1 -#define HAVE_UDP 2 -#define HAVE_TCP 4 -#define HAVE_ARP 8 - -#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data) -#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */ -#define IP_HDR_SIZE 20 -#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN) -#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44) - -/* various target states */ -#define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */ -#define TSTATE_WAITING 0x02 /* unanswered packets on the wire */ -#define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */ -#define TSTATE_UNREACH 0x08 - /** prototypes **/ -void print_help(void); +void print_help(); void print_usage(void); -static u_int get_timevar(const char * /*str*/); -static u_int get_timevaldiff(struct timeval * /*early*/, struct timeval * /*later*/); + +/* Time related */ +static unsigned int get_timevar(const char * /*str*/); +static time_t get_timevaldiff(struct timeval * /*early*/, struct timeval * /*later*/, + struct timeval *prog_start); + static in_addr_t get_ip_address(const char * /*ifname*/); -static int wait_for_reply(int /*sock*/, u_int /*t*/); -static int recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, struct sockaddr * /*saddr*/, u_int * /*timo*/, - struct timeval * /*tv*/); -static int send_icmp_ping(int /*sock*/, struct rta_host * /*host*/); +static void set_source_ip(char * /*arg*/, const int icmp_sock); + +/* Receiving data */ +static int wait_for_reply(int /*sock*/, unsigned int /*t*/, bool order_mode, bool mos_mode, + bool rta_mode, bool pl_mode, bool jitter_mode, bool score_mode, + int min_hosts_alive, unsigned short icmp_pkt_size, + unsigned int *pkt_interval, unsigned int *target_interval, threshold warn, + threshold crit, pid_t pid, int mode, + unsigned long long max_completion_time, struct timeval *prog_start, + struct rta_host **table, const unsigned short packets, + const int icmp_sock, const unsigned short number_of_targets, + check_icmp_state *program_state); + +static int recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, + struct sockaddr * /*saddr*/, unsigned int * /*timo*/, + struct timeval * /*tv*/, struct timeval *prog_start); +static int handle_random_icmp(unsigned char * /*packet*/, struct sockaddr_storage * /*addr*/, + unsigned int *pkt_interval, unsigned int *target_interval, pid_t pid, + struct rta_host **table, unsigned short packets, + const unsigned short number_of_targets, + check_icmp_state *program_state); + +/* Sending data */ +static int send_icmp_ping(int /*sock*/, struct rta_host * /*host*/, unsigned short icmp_pkt_size, + pid_t pid, check_icmp_state *program_state); + +/* Threshold related */ static int get_threshold(char *str, threshold *th); -static bool get_threshold2(char *str, size_t length, threshold * /*warn*/, threshold * /*crit*/, threshold_mode mode); +static bool get_threshold2(char *str, size_t length, threshold * /*warn*/, threshold * /*crit*/, + threshold_mode mode); static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); -static void run_checks(void); -static void set_source_ip(char * /*arg*/); -static int add_target(char * /*arg*/); + +/* main test function */ +static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, + bool jitter_mode, bool score_mode, int min_hosts_alive, + unsigned short icmp_pkt_size, unsigned int *pkt_interval, + unsigned int *target_interval, threshold warn, threshold crit, pid_t pid, + int mode, unsigned int max_completion_time, const struct timeval *prog_start, + struct rta_host **table, const unsigned short packets, const int icmp_sock, + const unsigned short number_of_targets, check_icmp_state *program_state); + +/* Target aquisition */ +static int add_target(char * /*arg*/, int mode); static int add_target_ip(char * /*arg*/, struct sockaddr_storage * /*in*/); -static int handle_random_icmp(unsigned char * /*packet*/, struct sockaddr_storage * /*addr*/); + static void parse_address(struct sockaddr_storage * /*addr*/, char * /*address*/, int /*size*/); + static unsigned short icmp_checksum(uint16_t * /*p*/, size_t /*n*/); -static void finish(int /*sig*/); + +/* End of run function */ +static void finish(int /*sig*/, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, + bool jitter_mode, bool score_mode, int min_hosts_alive, threshold warn, + threshold crit, const int icmp_sock, const unsigned short number_of_targets, + check_icmp_state *program_state); + +/* Error exit */ static void crash(const char * /*fmt*/, ...); /** global variables **/ -static struct rta_host **table, *cursor, *list; - -static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; -static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; - -static int mode, protocols, sockets, debug = 0, timeout = 10; -static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; -static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; - -static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0; -#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) -static unsigned short targets_down = 0, targets = 0, packets = 0; -#define targets_alive (targets - targets_down) -static unsigned int retry_interval, pkt_interval, target_interval; -static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK; -static pid_t pid; -static struct timezone tz; -static struct timeval prog_start; -static unsigned long long max_completion_time = 0; -static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ -static int min_hosts_alive = -1; -static float pkt_backoff_factor = 1.5; -static float target_backoff_factor = 1.5; -static bool rta_mode = false; -static bool pl_mode = false; -static bool jitter_mode = false; -static bool score_mode = false; -static bool mos_mode = false; -static bool order_mode = false; +static struct rta_host *cursor = NULL; +static struct rta_host *host_list = NULL; + +static int debug = 0; + +/** the working code **/ + +static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) { + return targets - targets_down; +} +static inline unsigned int icmp_pkts_en_route(unsigned int icmp_sent, unsigned int icmp_recv, + unsigned int icmp_lost) { + return icmp_sent - (icmp_recv + icmp_lost); +} + +// Create configuration from cli parameters +typedef struct { + int errorcode; + check_icmp_config config; +} check_icmp_config_wrapper; +check_icmp_config_wrapper process_arguments(int argc, char **argv); + +check_icmp_config_wrapper process_arguments(int argc, char **argv) { + /* get calling name the old-fashioned way for portability instead + * of relying on the glibc-ism __progname */ + char *ptr = strrchr(argv[0], '/'); + if (ptr) { + progname = &ptr[1]; + } else { + progname = argv[0]; + } + + check_icmp_config_wrapper result = { + .errorcode = OK, + .config = check_icmp_config_init(), + }; + + /* use the pid to mark packets as ours */ + /* Some systems have 32-bit pid_t so mask off only 16 bits */ + result.config.pid = getpid() & 0xffff; + + if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { + result.config.mode = MODE_ICMP; + } else if (!strcmp(progname, "check_host")) { + result.config.mode = MODE_HOSTCHECK; + result.config.pkt_interval = 1000000; + result.config.packets = 5; + result.config.crit.rta = result.config.warn.rta = 1000000; + result.config.crit.pl = result.config.warn.pl = 100; + } else if (!strcmp(progname, "check_rta_multi")) { + result.config.mode = MODE_ALL; + result.config.target_interval = 0; + result.config.pkt_interval = 50000; + result.config.packets = 5; + } + /* support "--help" and "--version" */ + if (argc == 2) { + if (!strcmp(argv[1], "--help")) { + strcpy(argv[1], "-h"); + } + if (!strcmp(argv[1], "--version")) { + strcpy(argv[1], "-V"); + } + } + + /* Parse protocol arguments first */ + char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64"; + for (int i = 1; i < argc; i++) { + long int arg; + while ((arg = getopt(argc, argv, opts_str)) != EOF) { + switch (arg) { + case '4': + if (address_family != -1) { + crash("Multiple protocol versions not supported"); + } + address_family = AF_INET; + break; + case '6': +#ifdef USE_IPV6 + if (address_family != -1) { + crash("Multiple protocol versions not supported"); + } + address_family = AF_INET6; +#else + usage(_("IPv6 support not available\n")); +#endif + break; + } + } + } + + /* Reset argument scanning */ + optind = 1; + + bool err; + /* parse the arguments */ + for (int i = 1; i < argc; i++) { + long int arg; + while ((arg = getopt(argc, argv, opts_str)) != EOF) { + switch (arg) { + case 'v': + debug++; + break; + case 'b': { + long size = strtol(optarg, NULL, 0); + if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && + size < MAX_PING_DATA) { + result.config.icmp_data_size = size; + result.config.icmp_pkt_size = size + ICMP_MINLEN; + } else { + usage_va("ICMP data length must be between: %lu and %lu", + sizeof(struct icmp) + sizeof(struct icmp_ping_data), + MAX_PING_DATA - 1); + } + } break; + case 'i': + result.config.pkt_interval = get_timevar(optarg); + break; + case 'I': + result.config.target_interval = get_timevar(optarg); + break; + case 'w': + get_threshold(optarg, &result.config.warn); + break; + case 'c': + get_threshold(optarg, &result.config.crit); + break; + case 'n': + case 'p': + result.config.packets = strtoul(optarg, NULL, 0); + if (result.config.packets > 20) { + errno = 0; + crash("packets is > 20 (%d)", result.config.packets); + } + break; + case 't': + result.config.timeout = strtoul(optarg, NULL, 0); + // TODO die here and complain about wrong input + // instead of: + if (!result.config.timeout) { + result.config.timeout = 10; + } + break; + case 'H': { + int add_result = add_target(optarg, result.config.mode); + if (add_result == 0) { + result.config.number_of_targets++; + } + } break; + case 'l': + result.config.ttl = strtoul(optarg, NULL, 0); + break; + case 'm': + result.config.min_hosts_alive = (int)strtoul(optarg, NULL, 0); + break; + case 's': /* specify source IP address */ + result.config.source_ip = optarg; + break; + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + case 'h': /* help */ + print_help(); + exit(STATE_UNKNOWN); + break; + case 'R': /* RTA mode */ + err = get_threshold2(optarg, strlen(optarg), &result.config.warn, + &result.config.crit, const_rta_mode); + if (!err) { + crash("Failed to parse RTA threshold"); + } + + result.config.rta_mode = true; + break; + case 'P': /* packet loss mode */ + err = get_threshold2(optarg, strlen(optarg), &result.config.warn, + &result.config.crit, const_packet_loss_mode); + if (!err) { + crash("Failed to parse packet loss threshold"); + } + + result.config.pl_mode = true; + break; + case 'J': /* jitter mode */ + err = get_threshold2(optarg, strlen(optarg), &result.config.warn, + &result.config.crit, const_jitter_mode); + if (!err) { + crash("Failed to parse jitter threshold"); + } + + result.config.jitter_mode = true; + break; + case 'M': /* MOS mode */ + err = get_threshold2(optarg, strlen(optarg), &result.config.warn, + &result.config.crit, const_mos_mode); + if (!err) { + crash("Failed to parse MOS threshold"); + } + + result.config.mos_mode = true; + break; + case 'S': /* score mode */ + err = get_threshold2(optarg, strlen(optarg), &result.config.warn, + &result.config.crit, const_score_mode); + if (!err) { + crash("Failed to parse score threshold"); + } + + result.config.score_mode = true; + break; + case 'O': /* out of order mode */ + result.config.order_mode = true; + break; + } + } + } + + argv = &argv[optind]; + while (*argv) { + add_target(*argv, result.config.mode); + argv++; + } + + if (!result.config.number_of_targets) { + errno = 0; + crash("No hosts to check"); + } + + /* stupid users should be able to give whatever thresholds they want + * (nothing will break if they do), but some anal plugin maintainer + * will probably add some printf() thing here later, so it might be + * best to at least show them where to do it. ;) */ + if (result.config.warn.pl > result.config.crit.pl) { + result.config.warn.pl = result.config.crit.pl; + } + if (result.config.warn.rta > result.config.crit.rta) { + result.config.warn.rta = result.config.crit.rta; + } + if (result.config.warn.jitter > result.config.crit.jitter) { + result.config.crit.jitter = result.config.warn.jitter; + } + if (result.config.warn.mos < result.config.crit.mos) { + result.config.warn.mos = result.config.crit.mos; + } + if (result.config.warn.score < result.config.crit.score) { + result.config.warn.score = result.config.crit.score; + } + + return result; +} /** code start **/ static void crash(const char *fmt, ...) { @@ -382,7 +567,11 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm return msg; } -static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { +static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, + unsigned int *pkt_interval, unsigned int *target_interval, + const pid_t pid, struct rta_host **table, unsigned short packets, + const unsigned short number_of_targets, + check_icmp_state *program_state) { struct icmp p; memcpy(&p, packet, sizeof(p)); if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { @@ -404,7 +593,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad * TIMXCEED actually sends a proper icmp response we will have passed * too many hops to have a hope of reaching it later, in which case it * indicates overconfidence in the network, poor routing or both. */ - if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { + if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && + p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { return 0; } @@ -412,7 +602,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad * to RFC 792). If it isn't, just ignore it */ struct icmp sent_icmp; memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); - if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) { + if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || + ntohs(sent_icmp.icmp_seq) >= number_of_targets * packets) { if (debug) { printf("Packet is no response to a packet we sent\n"); } @@ -424,10 +615,11 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad if (debug) { char address[INET6_ADDRSTRLEN]; parse_address(addr, address, sizeof(address)); - printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name); + printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", + get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name); } - icmp_lost++; + program_state->icmp_lost++; host->icmp_lost++; /* don't spend time on lost hosts any more */ if (host->flags & FLAG_LOST_CAUSE) { @@ -437,10 +629,10 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad /* source quench means we're sending too fast, so increase the * interval and mark this packet lost */ if (p.icmp_type == ICMP_SOURCEQUENCH) { - pkt_interval *= pkt_backoff_factor; - target_interval *= target_backoff_factor; + *pkt_interval *= PACKET_BACKOFF_FACTOR; + *target_interval *= TARGET_BACKOFF_FACTOR; } else { - targets_down++; + program_state->targets_down++; host->flags |= FLAG_LOST_CAUSE; } host->icmp_type = p.icmp_type; @@ -466,240 +658,23 @@ int main(int argc, char **argv) { bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - /* we only need to be setsuid when we get the sockets, so do - * that before pointer magic (esp. on network data) */ - int icmp_sockerrno; - int udp_sockerrno; - int tcp_sockerrno; - icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0; - address_family = -1; int icmp_proto = IPPROTO_ICMP; - /* get calling name the old-fashioned way for portability instead - * of relying on the glibc-ism __progname */ - char *ptr = strrchr(argv[0], '/'); - if (ptr) { - progname = &ptr[1]; - } else { - progname = argv[0]; - } - - /* now set defaults. Use progname to set them initially (allows for - * superfast check_host program when target host is up */ - cursor = list = NULL; - table = NULL; - - mode = MODE_RTA; - /* Default critical thresholds */ - crit.rta = 500000; - crit.pl = 80; - crit.jitter = 50; - crit.mos = 3; - crit.score = 70; - /* Default warning thresholds */ - warn.rta = 200000; - warn.pl = 40; - warn.jitter = 40; - warn.mos = 3.5; - warn.score = 80; - - protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP; - pkt_interval = 80000; /* 80 msec packet interval by default */ - packets = 5; - - if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { - mode = MODE_ICMP; - protocols = HAVE_ICMP; - } else if (!strcmp(progname, "check_host")) { - mode = MODE_HOSTCHECK; - pkt_interval = 1000000; - packets = 5; - crit.rta = warn.rta = 1000000; - crit.pl = warn.pl = 100; - } else if (!strcmp(progname, "check_rta_multi")) { - mode = MODE_ALL; - target_interval = 0; - pkt_interval = 50000; - packets = 5; - } - - /* support "--help" and "--version" */ - if (argc == 2) { - if (!strcmp(argv[1], "--help")) { - strcpy(argv[1], "-h"); - } - if (!strcmp(argv[1], "--version")) { - strcpy(argv[1], "-V"); - } - } - - /* Parse protocol arguments first */ - char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64"; - for (int i = 1; i < argc; i++) { - long int arg; - while ((arg = getopt(argc, argv, opts_str)) != EOF) { - switch (arg) { - case '4': - if (address_family != -1) { - crash("Multiple protocol versions not supported"); - } - address_family = AF_INET; - break; - case '6': -#ifdef USE_IPV6 - if (address_family != -1) { - crash("Multiple protocol versions not supported"); - } - address_family = AF_INET6; -#else - usage(_("IPv6 support not available\n")); -#endif - break; - } - } - } - - /* Reset argument scanning */ - optind = 1; - - unsigned long size; - bool err; - /* parse the arguments */ - char *source_ip = NULL; - for (int i = 1; i < argc; i++) { - long int arg; - while ((arg = getopt(argc, argv, opts_str)) != EOF) { - switch (arg) { - case 'v': - debug++; - break; - case 'b': - size = strtol(optarg, NULL, 0); - if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) { - icmp_data_size = size; - icmp_pkt_size = size + ICMP_MINLEN; - } else { - usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data), - MAX_PING_DATA - 1); - } - break; - case 'i': - pkt_interval = get_timevar(optarg); - break; - case 'I': - target_interval = get_timevar(optarg); - break; - case 'w': - get_threshold(optarg, &warn); - break; - case 'c': - get_threshold(optarg, &crit); - break; - case 'n': - case 'p': - packets = strtoul(optarg, NULL, 0); - break; - case 't': - timeout = strtoul(optarg, NULL, 0); - if (!timeout) { - timeout = 10; - } - break; - case 'H': - add_target(optarg); - break; - case 'l': - ttl = (int)strtoul(optarg, NULL, 0); - break; - case 'm': - min_hosts_alive = (int)strtoul(optarg, NULL, 0); - break; - case 'd': /* implement later, for cluster checks */ - warn_down = (unsigned char)strtoul(optarg, &ptr, 0); - if (ptr) { - crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0); - } - break; - case 's': /* specify source IP address */ - source_ip = optarg; - break; - case 'V': /* version */ - print_revision(progname, NP_VERSION); - exit(STATE_UNKNOWN); - case 'h': /* help */ - print_help(); - exit(STATE_UNKNOWN); - break; - case 'R': /* RTA mode */ - err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode); - if (!err) { - crash("Failed to parse RTA threshold"); - } - - rta_mode = true; - break; - case 'P': /* packet loss mode */ - err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode); - if (!err) { - crash("Failed to parse packet loss threshold"); - } - - pl_mode = true; - break; - case 'J': /* jitter mode */ - err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode); - if (!err) { - crash("Failed to parse jitter threshold"); - } - - jitter_mode = true; - break; - case 'M': /* MOS mode */ - err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode); - if (!err) { - crash("Failed to parse MOS threshold"); - } - - mos_mode = true; - break; - case 'S': /* score mode */ - err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode); - if (!err) { - crash("Failed to parse score threshold"); - } - - score_mode = true; - break; - case 'O': /* out of order mode */ - order_mode = true; - break; - } - } - } - /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ environ = NULL; - /* use the pid to mark packets as ours */ - /* Some systems have 32-bit pid_t so mask off only 16 bits */ - pid = getpid() & 0xffff; - /* printf("pid = %u\n", pid); */ - /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - argv = &argv[optind]; - while (*argv) { - add_target(*argv); - argv++; - } + check_icmp_config_wrapper tmp_config = process_arguments(argc, argv); - if (!targets) { - errno = 0; - crash("No hosts to check"); + if (tmp_config.errorcode != OK) { + crash("failed to parse config"); } + const check_icmp_config config = tmp_config.config; + // add_target might change address_family switch (address_family) { case AF_INET: @@ -711,14 +686,14 @@ int main(int argc, char **argv) { default: crash("Address family not supported"); } - if ((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1) { - sockets |= HAVE_ICMP; - } else { - icmp_sockerrno = errno; + + int icmp_sock = socket(address_family, SOCK_RAW, icmp_proto); + if (icmp_sock == -1) { + crash("Failed to obtain ICMP socket"); } - if (source_ip) { - set_source_ip(source_ip); + if (config.source_ip) { + set_source_ip(config.source_ip, icmp_sock); } #ifdef SO_TIMESTAMP @@ -736,184 +711,179 @@ int main(int argc, char **argv) { return 1; } - if (!sockets) { - if (icmp_sock == -1) { - errno = icmp_sockerrno; - crash("Failed to obtain ICMP socket"); - return -1; - } - /* if(udp_sock == -1) { */ - /* errno = icmp_sockerrno; */ - /* crash("Failed to obtain UDP socket"); */ - /* return -1; */ - /* } */ - /* if(tcp_sock == -1) { */ - /* errno = icmp_sockerrno; */ - /* crash("Failed to obtain TCP socker"); */ - /* return -1; */ - /* } */ - } - if (!ttl) { - ttl = 64; - } - if (icmp_sock) { - int result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl)); + int result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl)); if (debug) { if (result == -1) { printf("setsockopt failed\n"); } else { - printf("ttl set to %u\n", ttl); + printf("ttl set to %lu\n", config.ttl); } } } - /* stupid users should be able to give whatever thresholds they want - * (nothing will break if they do), but some anal plugin maintainer - * will probably add some printf() thing here later, so it might be - * best to at least show them where to do it. ;) */ - if (warn.pl > crit.pl) { - warn.pl = crit.pl; - } - if (warn.rta > crit.rta) { - warn.rta = crit.rta; - } - if (warn_down > crit_down) { - crit_down = warn_down; - } - if (warn.jitter > crit.jitter) { - crit.jitter = warn.jitter; - } - if (warn.mos < crit.mos) { - warn.mos = crit.mos; - } - if (warn.score < crit.score) { - warn.score = crit.score; - } - #ifdef HAVE_SIGACTION struct sigaction sig_action; sig_action.sa_sigaction = NULL; sig_action.sa_handler = finish; sigfillset(&sig_action.sa_mask); sig_action.sa_flags = SA_NODEFER | SA_RESTART; + sigaction(SIGINT, &sig_action, NULL); sigaction(SIGHUP, &sig_action, NULL); sigaction(SIGTERM, &sig_action, NULL); sigaction(SIGALRM, &sig_action, NULL); #else /* HAVE_SIGACTION */ - signal(SIGINT, finish); - signal(SIGHUP, finish); - signal(SIGTERM, finish); - signal(SIGALRM, finish); + // signal(SIGINT, finish); + // signal(SIGHUP, finish); + // signal(SIGTERM, finish); + // signal(SIGALRM, finish); #endif /* HAVE_SIGACTION */ if (debug) { - printf("Setting alarm timeout to %u seconds\n", timeout); + printf("Setting alarm timeout to %u seconds\n", config.timeout); } - alarm(timeout); + alarm(config.timeout); /* make sure we don't wait any longer than necessary */ - gettimeofday(&prog_start, &tz); - max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta; + struct timezone time_zone_dummy; + struct timeval prog_start; + gettimeofday(&prog_start, &time_zone_dummy); + + unsigned int max_completion_time = + ((config.number_of_targets * config.packets * config.pkt_interval) + + (config.number_of_targets * config.target_interval)) + + (config.number_of_targets * config.packets * config.crit.rta) + config.crit.rta; if (debug) { printf("packets: %u, targets: %u\n" "target_interval: %0.3f, pkt_interval %0.3f\n" "crit.rta: %0.3f\n" "max_completion_time: %0.3f\n", - packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000, + config.packets, config.number_of_targets, (float)config.target_interval / 1000, + (float)config.pkt_interval / 1000, (float)config.crit.rta / 1000, (float)max_completion_time / 1000); } if (debug) { - if (max_completion_time > (u_int)timeout * 1000000) { - printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout); - printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1); + if (max_completion_time > (unsigned int)config.timeout * 1000000) { + printf("max_completion_time: %u timeout: %u\n", max_completion_time, config.timeout); + printf("Timeout must be at least %u\n", (max_completion_time / 1000000) + 1); } } if (debug) { - printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl); - printf("pkt_interval: %u target_interval: %u retry_interval: %u\n", pkt_interval, target_interval, retry_interval); - printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout); + printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", config.crit.rta, config.crit.pl, + config.warn.rta, config.warn.pl); + printf("pkt_interval: %u target_interval: %u\n", config.pkt_interval, + config.target_interval); + printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_pkt_size, config.timeout); } - if (packets > 20) { + if (config.min_hosts_alive < -1) { errno = 0; - crash("packets is > 20 (%d)", packets); + crash("minimum alive hosts is negative (%i)", config.min_hosts_alive); } - if (min_hosts_alive < -1) { - errno = 0; - crash("minimum alive hosts is negative (%i)", min_hosts_alive); - } - - struct rta_host *host = list; - table = malloc(sizeof(struct rta_host *) * targets); + struct rta_host *host = host_list; + struct rta_host **table = malloc(sizeof(struct rta_host *) * config.number_of_targets); if (!table) { crash("main(): malloc failed for host table"); } - int i = 0; + unsigned short i = 0; while (host) { - host->id = i * packets; + host->id = i * config.packets; table[i] = host; host = host->next; i++; } - run_checks(); + unsigned int pkt_interval = config.pkt_interval; + unsigned int target_interval = config.target_interval; + + check_icmp_state program_state = check_icmp_state_init(); + + run_checks(config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, + config.jitter_mode, config.score_mode, config.min_hosts_alive, config.icmp_data_size, + &pkt_interval, &target_interval, config.warn, config.crit, config.pid, config.mode, + max_completion_time, &prog_start, table, config.packets, icmp_sock, + config.number_of_targets, &program_state); errno = 0; - finish(0); + finish(0, config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, + config.jitter_mode, config.score_mode, config.min_hosts_alive, config.warn, config.crit, + icmp_sock, config.number_of_targets, &program_state); return (0); } -static void run_checks(void) { +static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, + bool jitter_mode, bool score_mode, int min_hosts_alive, + unsigned short icmp_pkt_size, unsigned int *pkt_interval, + unsigned int *target_interval, threshold warn, threshold crit, + const pid_t pid, const int mode, const unsigned int max_completion_time, + const struct timeval *prog_start, struct rta_host **table, + const unsigned short packets, const int icmp_sock, + const unsigned short number_of_targets, check_icmp_state *program_state) { /* this loop might actually violate the pkt_interval or target_interval * settings, but only if there aren't any packets on the wire which * indicates that the target can handle an increased packet rate */ - for (u_int i = 0; i < packets; i++) { - for (u_int t = 0; t < targets; t++) { + for (unsigned int packet_index = 0; packet_index < packets; packet_index++) { + for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) { /* don't send useless packets */ - if (!targets_alive) { - finish(0); + if (!targets_alive(number_of_targets, program_state->targets_down)) { + finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, + min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state); } - if (table[t]->flags & FLAG_LOST_CAUSE) { + if (table[target_index]->flags & FLAG_LOST_CAUSE) { if (debug) { - printf("%s is a lost cause. not sending any more\n", table[t]->name); + printf("%s is a lost cause. not sending any more\n", table[target_index]->name); } continue; } /* we're still in the game, so send next packet */ - (void)send_icmp_ping(icmp_sock, table[t]); - wait_for_reply(icmp_sock, target_interval); + (void)send_icmp_ping(icmp_sock, table[target_index], icmp_pkt_size, pid, program_state); + + wait_for_reply(icmp_sock, *target_interval, order_mode, mos_mode, rta_mode, pl_mode, + jitter_mode, score_mode, min_hosts_alive, icmp_pkt_size, pkt_interval, + target_interval, warn, crit, pid, mode, max_completion_time, prog_start, + table, packets, icmp_sock, number_of_targets, program_state); } - wait_for_reply(icmp_sock, pkt_interval * targets); + wait_for_reply(icmp_sock, *pkt_interval * number_of_targets, order_mode, mos_mode, rta_mode, + pl_mode, jitter_mode, score_mode, min_hosts_alive, icmp_pkt_size, + pkt_interval, target_interval, warn, crit, pid, mode, max_completion_time, + prog_start, table, packets, icmp_sock, number_of_targets, program_state); } - if (icmp_pkts_en_route && targets_alive) { - u_int time_passed = get_timevaldiff(NULL, NULL); - u_int final_wait = max_completion_time - time_passed; + if (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv, + program_state->icmp_lost) && + targets_alive(number_of_targets, program_state->targets_down)) { + unsigned int time_passed = get_timevaldiff(NULL, NULL, prog_start); + unsigned int final_wait = max_completion_time - time_passed; if (debug) { - printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time); + printf("time_passed: %u final_wait: %u max_completion_time: %u\n", time_passed, + final_wait, max_completion_time); } if (time_passed > max_completion_time) { if (debug) { printf("Time passed. Finishing up\n"); } - finish(0); + finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, + min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state); } /* catch the packets that might come in within the timeframe, but * haven't yet */ if (debug) { - printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000); + printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, + (float)final_wait / 1000); } - wait_for_reply(icmp_sock, final_wait); + wait_for_reply(icmp_sock, final_wait, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, + score_mode, min_hosts_alive, icmp_pkt_size, pkt_interval, target_interval, + warn, crit, pid, mode, max_completion_time, prog_start, table, packets, + icmp_sock, number_of_targets, program_state); } } @@ -927,7 +897,15 @@ static void run_checks(void) { * both: * icmp echo reply : the rest */ -static int wait_for_reply(int sock, u_int t) { +static int wait_for_reply(int sock, const unsigned int time_interval, bool order_mode, + bool mos_mode, bool rta_mode, bool pl_mode, bool jitter_mode, + bool score_mode, int min_hosts_alive, unsigned short icmp_pkt_size, + unsigned int *pkt_interval, unsigned int *target_interval, threshold warn, + threshold crit, const pid_t pid, const int mode, + const unsigned long long max_completion_time, struct timeval *prog_start, + struct rta_host **table, const unsigned short packets, + const int icmp_sock, const unsigned short number_of_targets, + check_icmp_state *program_state) { union icmp_packet packet; if (!(packet.buf = malloc(icmp_pkt_size))) { crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); @@ -937,30 +915,39 @@ static int wait_for_reply(int sock, u_int t) { memset(packet.buf, 0, icmp_pkt_size); /* if we can't listen or don't have anything to listen to, just return */ - if (!t || !icmp_pkts_en_route) { + if (!time_interval || !icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv, + program_state->icmp_lost)) { free(packet.buf); return 0; } struct timeval wait_start; - gettimeofday(&wait_start, &tz); + struct timezone time_zone_dummy; + gettimeofday(&wait_start, &time_zone_dummy); - u_int i = t; struct sockaddr_storage resp_addr; - u_int per_pkt_wait = t / icmp_pkts_en_route; + unsigned int per_pkt_wait = + time_interval / icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv, + program_state->icmp_lost); static unsigned char buf[65536]; union ip_hdr *ip; struct timeval now; - while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) { - t = per_pkt_wait; + while (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv, + program_state->icmp_lost) && + get_timevaldiff(&wait_start, NULL, prog_start) < time_interval) { + unsigned int loop_time_interval = per_pkt_wait; /* wrap up if all targets are declared dead */ - if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) { - finish(0); + if (!targets_alive(number_of_targets, program_state->targets_down) || + get_timevaldiff(prog_start, NULL, prog_start) >= max_completion_time || + (mode == MODE_HOSTCHECK && program_state->targets_down)) { + finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, + min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state); } /* reap responses until we hit a timeout */ - int n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); + int n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, + &loop_time_interval, &now, prog_start); if (!n) { if (debug > 1) { printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); @@ -982,7 +969,9 @@ static int wait_for_reply(int sock, u_int t) { if (debug > 1) { char address[INET6_ADDRSTRLEN]; parse_address(&resp_addr, address, sizeof(address)); - printf("received %u bytes from %s\n", address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), address); + printf("received %u bytes from %s\n", + address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), + address); } } @@ -999,7 +988,8 @@ static int wait_for_reply(int sock, u_int t) { if (n < (hlen + ICMP_MINLEN)) { char address[INET6_ADDRSTRLEN]; parse_address(&resp_addr, address, sizeof(address)); - crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, hlen + icmp_pkt_size, address); + crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, + hlen + icmp_pkt_size, address); } /* else if(debug) { */ /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */ @@ -1013,14 +1003,19 @@ static int wait_for_reply(int sock, u_int t) { /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr) : sizeof(struct icmp));*/ - if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || - ntohs(packet.icp->icmp_seq) >= targets * packets)) || - (address_family == PF_INET6 && (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || - ntohs(packet.icp6->icmp6_seq) >= targets * packets))) { + if ((address_family == PF_INET && + (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || + ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) || + (address_family == PF_INET6 && + (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || + ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) { if (debug > 2) { printf("not a proper ICMP_ECHOREPLY\n"); } - handle_random_icmp(buf + hlen, &resp_addr); + + handle_random_icmp(buf + hlen, &resp_addr, pkt_interval, target_interval, pid, table, + packets, number_of_targets, program_state); + continue; } @@ -1030,20 +1025,22 @@ static int wait_for_reply(int sock, u_int t) { if (address_family == PF_INET) { memcpy(&data, packet.icp->icmp_data, sizeof(data)); if (debug > 2) { - printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data), ntohs(packet.icp->icmp_id), - ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); + printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data), + ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq), + packet.icp->icmp_cksum); } host = table[ntohs(packet.icp->icmp_seq) / packets]; } else { memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); if (debug > 2) { - printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data), ntohs(packet.icp6->icmp6_id), - ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); + printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data), + ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq), + packet.icp6->icmp6_cksum); } host = table[ntohs(packet.icp6->icmp6_seq) / packets]; } - u_int tdiff = get_timevaldiff(&data.stime, &now); + unsigned int tdiff = get_timevaldiff(&data.stime, &now, prog_start); if (host->last_tdiff > 0) { /* Calculate jitter */ @@ -1081,7 +1078,7 @@ static int wait_for_reply(int sock, u_int t) { host->time_waited += tdiff; host->icmp_recv++; - icmp_recv++; + program_state->icmp_recv++; if (tdiff > (unsigned int)host->rtmax) { host->rtmax = tdiff; @@ -1097,13 +1094,14 @@ static int wait_for_reply(int sock, u_int t) { switch (address_family) { case AF_INET: { - printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, - ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); + printf("%0.3f ms rtt from %s, incoming ttl: %u, max: %0.3f, min: %0.3f\n", + (float)tdiff / 1000, address, ip->ip.ip_ttl, (float)host->rtmax / 1000, + (float)host->rtmin / 1000); break; }; case AF_INET6: { - printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl, - (float)host->rtmax / 1000, (float)host->rtmin / 1000); + printf("%0.3f ms rtt from %s, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, + address, (float)host->rtmax / 1000, (float)host->rtmin / 1000); }; } } @@ -1112,7 +1110,8 @@ static int wait_for_reply(int sock, u_int t) { if (mode == MODE_HOSTCHECK) { printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|" "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n", - host->name, icmp_recv, (float)tdiff / 1000, icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000, + host->name, program_state->icmp_recv, (float)tdiff / 1000, + program_state->icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000); exit(STATE_OK); } @@ -1123,7 +1122,8 @@ static int wait_for_reply(int sock, u_int t) { } /* the ping functions */ -static int send_icmp_ping(int sock, struct rta_host *host) { +static int send_icmp_ping(const int sock, struct rta_host *host, const unsigned short icmp_pkt_size, + const pid_t pid, check_icmp_state *program_state) { if (sock == -1) { errno = 0; crash("Attempt to send on bogus socket"); @@ -1141,6 +1141,7 @@ static int send_icmp_ping(int sock, struct rta_host *host) { memset(buf, 0, icmp_pkt_size); struct timeval tv; + struct timezone tz; if ((gettimeofday(&tv, &tz)) == -1) { free(buf); return -1; @@ -1166,8 +1167,9 @@ static int send_icmp_ping(int sock, struct rta_host *host) { icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); if (debug > 2) { - printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", sizeof(data), ntohs(icp->icmp_id), - ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", + sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, + host->name); } } else { struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; @@ -1183,8 +1185,9 @@ static int send_icmp_ping(int sock, struct rta_host *host) { // let checksum be calculated automatically if (debug > 2) { - printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", sizeof(data), ntohs(icp6->icmp6_id), - ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", + sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, + host->name); } } @@ -1222,13 +1225,14 @@ static int send_icmp_ping(int sock, struct rta_host *host) { return -1; } - icmp_sent++; + program_state->icmp_sent++; host->icmp_sent++; return 0; } -static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { +static int recvfrom_wto(const int sock, void *buf, const unsigned int len, struct sockaddr *saddr, + unsigned int *timeout, struct timeval *tv, struct timeval *prog_start) { #ifdef HAVE_MSGHDR_MSG_CONTROL char ans_data[4096]; #endif // HAVE_MSGHDR_MSG_CONTROL @@ -1236,16 +1240,16 @@ static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr * struct cmsghdr *chdr; #endif - if (!*timo) { + if (!*timeout) { if (debug) { - printf("*timo is not\n"); + printf("*timeout is not\n"); } return 0; } struct timeval to; - to.tv_sec = *timo / 1000000; - to.tv_usec = (*timo - (to.tv_sec * 1000000)); + to.tv_sec = *timeout / 1000000; + to.tv_usec = (*timeout - (to.tv_sec * 1000000)); fd_set rd; fd_set wr; @@ -1255,54 +1259,68 @@ static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr * errno = 0; struct timeval then; - gettimeofday(&then, &tz); + struct timezone time_zone_dummy; + gettimeofday(&then, &time_zone_dummy); + int n = select(sock + 1, &rd, &wr, NULL, &to); if (n < 0) { crash("select() in recvfrom_wto"); } + struct timeval now; - gettimeofday(&now, &tz); - *timo = get_timevaldiff(&then, &now); + gettimeofday(&now, &time_zone_dummy); + *timeout = get_timevaldiff(&then, &now, prog_start); if (!n) { return 0; /* timeout */ } - u_int slen = sizeof(struct sockaddr_storage); + unsigned int slen = sizeof(struct sockaddr_storage); - struct iovec iov; - memset(&iov, 0, sizeof(iov)); - iov.iov_base = buf; - iov.iov_len = len; + struct iovec iov = { + .iov_base = buf, + .iov_len = len, + }; - struct msghdr hdr; - memset(&hdr, 0, sizeof(hdr)); - hdr.msg_name = saddr; - hdr.msg_namelen = slen; - hdr.msg_iov = &iov; - hdr.msg_iovlen = 1; + struct msghdr hdr = { + .msg_name = saddr, + .msg_namelen = slen, + .msg_iov = &iov, + .msg_iovlen = 1, #ifdef HAVE_MSGHDR_MSG_CONTROL - hdr.msg_control = ans_data; - hdr.msg_controllen = sizeof(ans_data); + .msg_control = ans_data, + .msg_controllen = sizeof(ans_data), #endif + }; + + ssize_t ret = recvmsg(sock, &hdr, 0); - int ret = recvmsg(sock, &hdr, 0); #ifdef SO_TIMESTAMP for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { - if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { + if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && + chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { memcpy(tv, CMSG_DATA(chdr), sizeof(*tv)); break; } } - if (!chdr) + if (!chdr) { + gettimeofday(tv, &time_zone_dummy); + } +#else + gettimeofday(tv, &time_zone_dummy); #endif // SO_TIMESTAMP - gettimeofday(tv, &tz); + return (ret); } -static void finish(int sig) { +static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, + bool jitter_mode, bool score_mode, int min_hosts_alive, threshold warn, + threshold crit, const int icmp_sock, const unsigned short number_of_targets, + check_icmp_state *program_state) { + // Deactivate alarm alarm(0); + if (debug > 1) { printf("finish(%d) called\n", sig); } @@ -1310,57 +1328,56 @@ static void finish(int sig) { if (icmp_sock != -1) { close(icmp_sock); } - if (udp_sock != -1) { - close(udp_sock); - } - if (tcp_sock != -1) { - close(tcp_sock); - } if (debug) { - printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost); - printf("targets: %u targets_alive: %u\n", targets, targets_alive); + printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", program_state->icmp_sent, + program_state->icmp_recv, program_state->icmp_lost); + printf("targets: %u targets_alive: %u\n", number_of_targets, + targets_alive(number_of_targets, program_state->targets_down)); } /* iterate thrice to calculate values, give output, and print perfparse */ - status = STATE_OK; - struct rta_host *host = list; + mp_state_enum status = STATE_OK; + struct rta_host *host = host_list; - u_int i = 0; + unsigned int target_counter = 0; const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"}; int hosts_ok = 0; int hosts_warn = 0; while (host) { - int this_status = STATE_OK; + mp_state_enum this_status = STATE_OK; - unsigned char pl; + unsigned char packet_loss; double rta; if (!host->icmp_recv) { /* rta 0 is ofcourse not entirely correct, but will still show up * conspicuously as missing entries in perfparse and cacti */ - pl = 100; + packet_loss = 100; rta = 0; status = STATE_CRITICAL; /* up the down counter if not already counted */ - if (!(host->flags & FLAG_LOST_CAUSE) && targets_alive) { - targets_down++; + if (!(host->flags & FLAG_LOST_CAUSE) && + targets_alive(number_of_targets, program_state->targets_down)) { + program_state->targets_down++; } } else { - pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent; + packet_loss = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent; rta = (double)host->time_waited / host->icmp_recv; } if (host->icmp_recv > 1) { /* * This algorithm is probably pretty much blindly copied from - * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos - * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good). - * According to some quick research MOS originates from the Audio/Video transport network area. - * Whether it can and should be computed from ICMP data, I can not say. + * locations like this one: + * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS + * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick + * research MOS originates from the Audio/Video transport network area. Whether it can + * and should be computed from ICMP data, I can not say. * * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value * - * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score ) + * MOS stands likely for Mean Opinion Score ( + * https://en.wikipedia.org/wiki/Mean_Opinion_Score ) * * More links: * - https://confluence.slac.stanford.edu/display/IEPM/MOS @@ -1383,7 +1400,7 @@ static void finish(int sig) { // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a // loss of 5% will be entered as 5). - R = R - (pl * 2.5); + R = R - (packet_loss * 2.5); if (R < 0) { R = 0; @@ -1398,7 +1415,7 @@ static void finish(int sig) { host->mos = 0; } - host->pl = pl; + host->pl = packet_loss; host->rta = rta; /* if no new mode selected, use old schema */ @@ -1421,11 +1438,11 @@ static void finish(int sig) { } if (pl_mode) { - if (pl >= crit.pl) { + if (packet_loss >= crit.pl) { this_status = STATE_CRITICAL; status = STATE_CRITICAL; host->pl_status = STATE_CRITICAL; - } else if (status != STATE_CRITICAL && (pl >= warn.pl)) { + } else if (status != STATE_CRITICAL && (packet_loss >= warn.pl)) { this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status); status = STATE_WARNING; host->pl_status = STATE_WARNING; @@ -1478,7 +1495,7 @@ static void finish(int sig) { } /* this is inevitable */ - if (!targets_alive) { + if (!targets_alive(number_of_targets, program_state->targets_down)) { status = STATE_CRITICAL; } if (min_hosts_alive > -1) { @@ -1490,21 +1507,21 @@ static void finish(int sig) { } printf("%s - ", status_string[status]); - host = list; + host = host_list; while (host) { if (debug) { puts(""); } - if (i) { - if (i < targets) { + if (target_counter) { + if (target_counter < number_of_targets) { printf(" :: "); } else { printf("\n"); } } - i++; + target_counter++; if (!host->icmp_recv) { status = STATE_CRITICAL; @@ -1514,7 +1531,8 @@ static void finish(int sig) { if (host->flags & FLAG_LOST_CAUSE) { char address[INET6_ADDRSTRLEN]; parse_address(&host->error_addr, address, sizeof(address)); - printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100); + printf("%s: %s @ %s. rta nan, lost %d%%", host->name, + get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100); } else { /* not marked as lost cause, so we have no flags for it */ printf("%s: rta nan, lost 100%%", host->name); } @@ -1525,9 +1543,11 @@ static void finish(int sig) { if (status == STATE_OK) { printf(" rta %0.3fms", host->rta / 1000); } else if (status == STATE_WARNING && host->rta_status == status) { - printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta / 1000); + printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, + (float)warn.rta / 1000); } else if (status == STATE_CRITICAL && host->rta_status == status) { - printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta / 1000); + printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, + (float)crit.rta / 1000); } } @@ -1591,8 +1611,8 @@ static void finish(int sig) { if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) { printf("|"); } - i = 0; - host = list; + target_counter = 0; + host = host_list; while (host) { if (debug) { puts(""); @@ -1600,45 +1620,56 @@ static void finish(int sig) { if (rta_mode) { if (host->pl < 100) { - printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "", - host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, (targets > 1) ? host->name : "", - (float)host->rtmax / 1000, (targets > 1) ? host->name : "", + printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", + (number_of_targets > 1) ? host->name : "", host->rta / 1000, + (float)warn.rta / 1000, (float)crit.rta / 1000, + (number_of_targets > 1) ? host->name : "", (float)host->rtmax / 1000, + (number_of_targets > 1) ? host->name : "", (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0); } else { - printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "", - (targets > 1) ? host->name : ""); + printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", + (number_of_targets > 1) ? host->name : "", + (number_of_targets > 1) ? host->name : "", + (number_of_targets > 1) ? host->name : ""); } } if (pl_mode) { - printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl); + printf("%spl=%u%%;%u;%u;0;100 ", (number_of_targets > 1) ? host->name : "", host->pl, + warn.pl, crit.pl); } if (jitter_mode) { if (host->pl < 100) { - printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ", - (targets > 1) ? host->name : "", (float)host->jitter, (float)warn.jitter, (float)crit.jitter, - (targets > 1) ? host->name : "", (float)host->jitter_max / 1000, (targets > 1) ? host->name : "", - (float)host->jitter_min / 1000); + printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; " + "%sjitter_min=%0.3fms;;;; ", + (number_of_targets > 1) ? host->name : "", (float)host->jitter, + (float)warn.jitter, (float)crit.jitter, + (number_of_targets > 1) ? host->name : "", (float)host->jitter_max / 1000, + (number_of_targets > 1) ? host->name : "", (float)host->jitter_min / 1000); } else { - printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets > 1) ? host->name : "", - (targets > 1) ? host->name : "", (targets > 1) ? host->name : ""); + printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", + (number_of_targets > 1) ? host->name : "", + (number_of_targets > 1) ? host->name : "", + (number_of_targets > 1) ? host->name : ""); } } if (mos_mode) { if (host->pl < 100) { - printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (targets > 1) ? host->name : "", (float)host->mos, (float)warn.mos, (float)crit.mos); + printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (number_of_targets > 1) ? host->name : "", + (float)host->mos, (float)warn.mos, (float)crit.mos); } else { - printf("%smos=U;;;; ", (targets > 1) ? host->name : ""); + printf("%smos=U;;;; ", (number_of_targets > 1) ? host->name : ""); } } if (score_mode) { if (host->pl < 100) { - printf("%sscore=%u;%u;%u;0;100 ", (targets > 1) ? host->name : "", (int)host->score, (int)warn.score, (int)crit.score); + printf("%sscore=%u;%u;%u;0;100 ", (number_of_targets > 1) ? host->name : "", + (int)host->score, (int)warn.score, (int)crit.score); } else { - printf("%sscore=U;;;; ", (targets > 1) ? host->name : ""); + printf("%sscore=U;;;; ", (number_of_targets > 1) ? host->name : ""); } } @@ -1656,46 +1687,53 @@ static void finish(int sig) { /* finish with an empty line */ puts(""); if (debug) { - printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", targets, targets_alive, hosts_ok, - hosts_warn, min_hosts_alive); + printf( + "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", + number_of_targets, targets_alive(number_of_targets, program_state->targets_down), + hosts_ok, hosts_warn, min_hosts_alive); } exit(status); } -static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { +static time_t get_timevaldiff(struct timeval *earlier, struct timeval *later, + struct timeval *prog_start) { struct timeval now; if (!later) { - gettimeofday(&now, &tz); + struct timezone time_zone_dummy; + gettimeofday(&now, &time_zone_dummy); later = &now; } - if (!early) { - early = &prog_start; + if (!earlier) { + earlier = prog_start; } /* if early > later we return 0 so as to indicate a timeout */ - if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) { + if (earlier->tv_sec > later->tv_sec || + (earlier->tv_sec == later->tv_sec && earlier->tv_usec > later->tv_usec)) { return 0; } - u_int ret = (later->tv_sec - early->tv_sec) * 1000000; - ret += later->tv_usec - early->tv_usec; + + time_t ret = (later->tv_sec - earlier->tv_sec) * 1000000; + ret += later->tv_usec - earlier->tv_usec; return ret; } -static int add_target_ip(char *arg, struct sockaddr_storage *in) { +static int add_target_ip(char *arg, struct sockaddr_storage *address) { struct sockaddr_in *sin; struct sockaddr_in6 *sin6; if (address_family == AF_INET) { - sin = (struct sockaddr_in *)in; + sin = (struct sockaddr_in *)address; } else { - sin6 = (struct sockaddr_in6 *)in; + sin6 = (struct sockaddr_in6 *)address; } /* disregard obviously stupid addresses * (I didn't find an ipv6 equivalent to INADDR_NONE) */ - if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || + if (((address_family == AF_INET && + (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { return -1; } @@ -1703,13 +1741,14 @@ static int add_target_ip(char *arg, struct sockaddr_storage *in) { /* no point in adding two identical IP's, so don't. ;) */ struct sockaddr_in *host_sin; struct sockaddr_in6 *host_sin6; - struct rta_host *host = list; + struct rta_host *host = host_list; while (host) { host_sin = (struct sockaddr_in *)&host->saddr_in; host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) || - (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { + (address_family == AF_INET6 && + host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { if (debug) { printf("Identical IP already exists. Not adding %s\n", arg); } @@ -1722,10 +1761,11 @@ static int add_target_ip(char *arg, struct sockaddr_storage *in) { host = (struct rta_host *)malloc(sizeof(struct rta_host)); if (!host) { char straddr[INET6_ADDRSTRLEN]; - parse_address((struct sockaddr_storage *)&in, straddr, sizeof(straddr)); + parse_address((struct sockaddr_storage *)&address, straddr, sizeof(straddr)); crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host)); } - memset(host, 0, sizeof(struct rta_host)); + + *host = ping_target_init(); /* set the values. use calling name for output */ host->name = strdup(arg); @@ -1738,39 +1778,23 @@ static int add_target_ip(char *arg, struct sockaddr_storage *in) { } else { host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; host_sin6->sin6_family = AF_INET6; - memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr); - } - - /* fill out the sockaddr_in struct */ - host->rtmin = INFINITY; - host->rtmax = 0; - host->jitter = 0; - host->jitter_max = 0; - host->jitter_min = INFINITY; - host->last_tdiff = 0; - host->order_status = STATE_OK; - host->last_icmp_seq = 0; - host->rta_status = 0; - host->pl_status = 0; - host->jitter_status = 0; - host->mos_status = 0; - host->score_status = 0; - host->pl_status = 0; - - if (!list) { - list = cursor = host; + memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, + sizeof host_sin6->sin6_addr.s6_addr); + } + + if (!host_list) { + host_list = cursor = host; } else { cursor->next = host; } cursor = host; - targets++; return 0; } /* wrapper for add_target_ip */ -static int add_target(char *arg) { +static int add_target(char *arg, const int mode) { struct sockaddr_storage ip; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; @@ -1850,7 +1874,7 @@ static int add_target(char *arg) { return 0; } -static void set_source_ip(char *arg) { +static void set_source_ip(char *arg, const int icmp_sock) { struct sockaddr_in src; memset(&src, 0, sizeof(src)); @@ -1894,7 +1918,7 @@ static in_addr_t get_ip_address(const char *ifname) { * s = seconds * return value is in microseconds */ -static u_int get_timevar(const char *str) { +static unsigned int get_timevar(const char *str) { if (!str) { return 0; } @@ -1920,7 +1944,7 @@ static u_int get_timevar(const char *str) { printf("evaluating %s, u: %c, p: %c\n", str, u, p); } - u_int factor = 1000; /* default to milliseconds */ + unsigned int factor = 1000; /* default to milliseconds */ if (u == 'u') { factor = 1; /* microseconds */ } else if (u == 'm') { @@ -1933,7 +1957,7 @@ static u_int get_timevar(const char *str) { } char *ptr; - u_int i; + unsigned int i; i = strtoul(str, &ptr, 0); if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { return i * factor; @@ -1945,7 +1969,7 @@ static u_int get_timevar(const char *str) { } /* integer and decimal, respectively */ - u_int d = strtoul(ptr + 1, NULL, 0); + unsigned int d = strtoul(ptr + 1, NULL, 0); /* d is decimal, so get rid of excess digits */ while (d >= factor) { @@ -2000,9 +2024,11 @@ static int get_threshold(char *str, threshold *th) { * @param[in] length strlen(str) * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned - * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively) + * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score + * (exclusively) */ -static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) { +static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, + threshold_mode mode) { if (!str || !length || !warn || !crit) { return false; } @@ -2108,13 +2134,14 @@ void print_help(void) { printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); printf(" %s\n", "-w"); printf(" %s", _("warning threshold (currently ")); - printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); + printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL); printf(" %s\n", "-c"); printf(" %s", _("critical threshold (currently ")); - printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl); + printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL); printf(" %s\n", "-R"); - printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); + printf(" %s\n", + _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); printf(" %s\n", "-P"); printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); printf(" %s\n", "-J"); @@ -2131,28 +2158,29 @@ void print_help(void) { printf(" %s\n", _("specify a source IP address or device name")); printf(" %s\n", "-n"); printf(" %s", _("number of packets to send (currently ")); - printf("%u)\n", packets); + printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS); printf(" %s\n", "-p"); printf(" %s", _("number of packets to send (currently ")); - printf("%u)\n", packets); + printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS); printf(" %s\n", "-i"); printf(" %s", _("max packet interval (currently ")); - printf("%0.3fms)\n", (float)pkt_interval / 1000); + printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000); printf(" %s\n", "-I"); printf(" %s", _("max target interval (currently ")); - printf("%0.3fms)\n", (float)target_interval / 1000); + printf("%0.3fms)\n", (float)DEFAULT_TARGET_INTERVAL / 1000); printf(" %s\n", "-m"); printf(" %s", _("number of alive hosts required for success")); printf("\n"); printf(" %s\n", "-l"); printf(" %s", _("TTL on outgoing packets (currently ")); - printf("%u)\n", ttl); + printf("%u)\n", DEFAULT_TTL); printf(" %s\n", "-t"); printf(" %s", _("timeout value (seconds, currently ")); - printf("%u)\n", timeout); + printf("%u)\n", DEFAULT_TIMEOUT); printf(" %s\n", "-b"); printf(" %s\n", _("Number of icmp data bytes to send")); - printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN); + printf(" %s %lu + %d)\n", _("Packet size will be data bytes + icmp header (currently"), + DEFAULT_PING_DATA_SIZE, ICMP_MINLEN); printf(" %s\n", "-v"); printf(" %s\n", _("verbose")); printf("\n"); @@ -2162,12 +2190,15 @@ void print_help(void) { printf("\n"); printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); printf(" %s\n", _("packet loss. The default values should work well for most users.")); - printf(" %s\n", _("You can specify different RTA factors using the standardized abbreviations")); - printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); + printf(" %s\n", + _("You can specify different RTA factors using the standardized abbreviations")); + printf(" %s\n", + _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); /* -d not yet implemented */ - /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops")); - printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent.")); - printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/ + /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 + hops")); printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent.")); printf + ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do + not."));*/ printf("\n"); printf(" %s\n", _("The -v switch can be specified several times for increased verbosity.")); /* printf ("%s\n", _("Long options are currently unsupported.")); diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c new file mode 100644 index 00000000..8f6d7362 --- /dev/null +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -0,0 +1,142 @@ +#include "./config.h" +#include "states.h" +#include +#include +#include "./check_icmp_helpers.h" +#include "../../plugins/netutils.h" + +check_icmp_config check_icmp_config_init() { + check_icmp_config tmp = { + .source_ip = NULL, + + .order_mode = false, + .mos_mode = false, + .rta_mode = false, + .pl_mode = false, + .jitter_mode = false, + .score_mode = false, + + .min_hosts_alive = -1, + .icmp_data_size = DEFAULT_PING_DATA_SIZE, + .icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN, + .pkt_interval = DEFAULT_PKT_INTERVAL, + .target_interval = 0, + .crit = {.pl = DEFAULT_CRIT_PL, + .rta = DEFAULT_CRIT_RTA, + .jitter = 50.0, + .mos = 3.0, + .score = 70.0}, + .warn = {.pl = DEFAULT_WARN_PL, + .rta = DEFAULT_WARN_RTA, + .jitter = 40.0, + .mos = 3.5, + .score = 80.0}, + .pid = {}, + .mode = MODE_RTA, + .timeout = DEFAULT_TIMEOUT, + .ttl = DEFAULT_TTL, + + .packets = DEFAULT_NUMBER_OF_PACKETS, + .number_of_targets = 0, + .hosts = NULL, + }; + return tmp; +} + +ping_target ping_target_init() { + ping_target tmp = { + .rtmin = INFINITY, + + .jitter_min = INFINITY, + + .rta_status = STATE_OK, + .jitter_status = STATE_OK, + .mos_status = STATE_OK, + .score_status = STATE_OK, + .pl_status = STATE_OK, + .order_status = STATE_OK, + }; + + return tmp; +} + +check_icmp_state check_icmp_state_init() { + check_icmp_state tmp = {.icmp_sent = 0, .icmp_lost = 0, .icmp_recv = 0, .targets_down = 0}; + + return tmp; +} + +rta_host_create_wrapper rta_host_create(char *name, struct sockaddr_storage *address) { + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + if (address_family == AF_INET) { + sin = (struct sockaddr_in *)address; + } else { + sin6 = (struct sockaddr_in6 *)address; + } + + rta_host_create_wrapper result = { + .errorcode = OK, + }; + + /* disregard obviously stupid addresses + * (I didn't find an ipv6 equivalent to INADDR_NONE) */ + if (((address_family == AF_INET && + (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || + (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { + result.errorcode = ERROR; + return result; + } + + // TODO: Maybe add the following back in as a sanity check for the config + // /* no point in adding two identical IP's, so don't. ;) */ + // struct sockaddr_in *host_sin; + // struct sockaddr_in6 *host_sin6; + // struct rta_host *host = host_list; + + // while (host) { + // host_sin = (struct sockaddr_in *)&host->saddr_in; + // host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; + + // if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) || + // (address_family == AF_INET6 && + // host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { + // if (debug) { + // printf("Identical IP already exists. Not adding %s\n", name); + // } + // return -1; + // } + // host = host->next; + // } + + /* add the fresh ip */ + ping_target host = ping_target_init(); + + /* set the values. use calling name for output */ + host.name = strdup(name); + + /* fill out the sockaddr_storage struct */ + if (address_family == AF_INET) { + struct sockaddr_in *host_sin = (struct sockaddr_in *)&host.saddr_in; + host_sin->sin_family = AF_INET; + host_sin->sin_addr.s_addr = sin->sin_addr.s_addr; + } else { + struct sockaddr_in6 *host_sin6 = (struct sockaddr_in6 *)&host.saddr_in; + host_sin6->sin6_family = AF_INET6; + memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, + sizeof host_sin6->sin6_addr.s6_addr); + } + + result.host = host; + + return result; +} + +check_icmp_target_container check_icmp_target_container_init() { + check_icmp_target_container tmp = { + .name = NULL, + .number_of_targets = 0, + .target_list = NULL, + }; + return tmp; +} diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h new file mode 100644 index 00000000..49f720ec --- /dev/null +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -0,0 +1,73 @@ +#pragma once + +#include "../../lib/states.h" +#include +#include +#include +#include +#include +#include +#include + +typedef struct rta_host { + unsigned short id; /* id in **table, and icmp pkts */ + char *name; /* arg used for adding this host */ + char *msg; /* icmp error message, if any */ + struct sockaddr_storage saddr_in; /* the address of this host */ + struct sockaddr_storage error_addr; /* stores address of error replies */ + unsigned long long time_waited; /* total time waited, in usecs */ + unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ + unsigned char icmp_type, icmp_code; /* type and code from errors */ + unsigned short flags; /* control/status flags */ + + double rta; /* measured RTA */ + double rtmax; /* max rtt */ + double rtmin; /* min rtt */ + + double jitter; /* measured jitter */ + double jitter_max; /* jitter rtt maximum */ + double jitter_min; /* jitter rtt minimum */ + + double EffectiveLatency; + double mos; /* Mean opinion score */ + double score; /* score */ + + unsigned int last_tdiff; + unsigned int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */ + unsigned char pl; /* measured packet loss */ + + mp_state_enum rta_status; // check result for RTA checks + mp_state_enum jitter_status; // check result for Jitter checks + mp_state_enum mos_status; // check result for MOS checks + mp_state_enum score_status; // check result for score checks + mp_state_enum pl_status; // check result for packet loss checks + mp_state_enum order_status; // check result for packet order checks + + struct rta_host *next; +} ping_target; + +ping_target ping_target_init(); + +typedef struct { + char *name; + ping_target *target_list; + unsigned int number_of_targets; +} check_icmp_target_container; + +check_icmp_target_container check_icmp_target_container_init(); + +typedef struct { + unsigned int icmp_sent; + unsigned int icmp_recv; + unsigned int icmp_lost; + unsigned short targets_down; +} check_icmp_state; + +check_icmp_state check_icmp_state_init(); + +typedef struct { + int errorcode; + ping_target host; +} rta_host_create_wrapper; + +rta_host_create_wrapper rta_host_create(char *name, struct sockaddr_storage *address); -- cgit v1.2.3-74-g34f1 From 5a6adcb7db497fba7b89471a6d58dba80330ff4a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 4 May 2025 01:42:52 +0200 Subject: WIP - check_icmp refactor 2 --- plugins-root/check_icmp.c | 724 +++++++++++++------------ plugins-root/check_icmp.d/check_icmp_helpers.c | 48 +- plugins-root/check_icmp.d/check_icmp_helpers.h | 7 +- plugins-root/check_icmp.d/config.h | 102 ++++ 4 files changed, 540 insertions(+), 341 deletions(-) create mode 100644 plugins-root/check_icmp.d/config.h diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 87dac21d..99414014 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -66,6 +66,9 @@ const char *email = "devel@monitoring-plugins.org"; #include #include #include +#include +#include +#include #include "../lib/states.h" #include "./check_icmp.d/config.h" @@ -131,76 +134,79 @@ void print_usage(void); /* Time related */ static unsigned int get_timevar(const char * /*str*/); -static time_t get_timevaldiff(struct timeval * /*early*/, struct timeval * /*later*/, - struct timeval *prog_start); +static time_t get_timevaldiff(struct timeval earlier, struct timeval later); +static time_t get_timevaldiff_to_now(struct timeval earlier); static in_addr_t get_ip_address(const char * /*ifname*/); -static void set_source_ip(char * /*arg*/, const int icmp_sock); +static void set_source_ip(char * /*arg*/, int icmp_sock); /* Receiving data */ -static int wait_for_reply(int /*sock*/, unsigned int /*t*/, bool order_mode, bool mos_mode, - bool rta_mode, bool pl_mode, bool jitter_mode, bool score_mode, - int min_hosts_alive, unsigned short icmp_pkt_size, - unsigned int *pkt_interval, unsigned int *target_interval, threshold warn, - threshold crit, pid_t pid, int mode, - unsigned long long max_completion_time, struct timeval *prog_start, - struct rta_host **table, const unsigned short packets, - const int icmp_sock, const unsigned short number_of_targets, - check_icmp_state *program_state); - -static int recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, - struct sockaddr * /*saddr*/, unsigned int * /*timo*/, - struct timeval * /*tv*/, struct timeval *prog_start); +static int wait_for_reply(int socket, time_t time_interval, unsigned short icmp_pkt_size, + unsigned int *pkt_interval, unsigned int *target_interval, pid_t pid, + ping_target **table, unsigned short packets, + unsigned short number_of_targets, check_icmp_state *program_state); + +static ssize_t recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, + struct sockaddr * /*saddr*/, time_t *timeout, struct timeval * /*tv*/); static int handle_random_icmp(unsigned char * /*packet*/, struct sockaddr_storage * /*addr*/, unsigned int *pkt_interval, unsigned int *target_interval, pid_t pid, - struct rta_host **table, unsigned short packets, - const unsigned short number_of_targets, - check_icmp_state *program_state); + ping_target **table, unsigned short packets, + unsigned short number_of_targets, check_icmp_state *program_state); /* Sending data */ -static int send_icmp_ping(int /*sock*/, struct rta_host * /*host*/, unsigned short icmp_pkt_size, +static int send_icmp_ping(int /*sock*/, ping_target * /*host*/, unsigned short icmp_pkt_size, pid_t pid, check_icmp_state *program_state); /* Threshold related */ -static int get_threshold(char *str, threshold *th); +static int get_threshold(char *str, threshold *threshold); static bool get_threshold2(char *str, size_t length, threshold * /*warn*/, threshold * /*crit*/, threshold_mode mode); -static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); +static bool parse_threshold2_helper(char *threshold_string, size_t length, threshold *thr, + threshold_mode mode); /* main test function */ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, bool jitter_mode, bool score_mode, int min_hosts_alive, unsigned short icmp_pkt_size, unsigned int *pkt_interval, unsigned int *target_interval, threshold warn, threshold crit, pid_t pid, - int mode, unsigned int max_completion_time, const struct timeval *prog_start, - struct rta_host **table, const unsigned short packets, const int icmp_sock, - const unsigned short number_of_targets, check_icmp_state *program_state); + int mode, unsigned int max_completion_time, struct timeval prog_start, + ping_target **table, unsigned short packets, int icmp_sock, + unsigned short number_of_targets, check_icmp_state *program_state, + ping_target *target_list); /* Target aquisition */ -static int add_target(char * /*arg*/, int mode); -static int add_target_ip(char * /*arg*/, struct sockaddr_storage * /*in*/); +typedef struct { + int error_code; + ping_target *targets; + unsigned int number_of_targets; +} add_target_wrapper; +static add_target_wrapper add_target(char * /*arg*/, int mode); + +typedef struct { + int error_code; + ping_target *target; +} add_target_ip_wrapper; +static add_target_ip_wrapper add_target_ip(char * /*arg*/, struct sockaddr_storage * /*in*/); -static void parse_address(struct sockaddr_storage * /*addr*/, char * /*address*/, int /*size*/); +static void parse_address(struct sockaddr_storage * /*addr*/, char * /*address*/, socklen_t size); static unsigned short icmp_checksum(uint16_t * /*p*/, size_t /*n*/); /* End of run function */ static void finish(int /*sig*/, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, bool jitter_mode, bool score_mode, int min_hosts_alive, threshold warn, - threshold crit, const int icmp_sock, const unsigned short number_of_targets, - check_icmp_state *program_state); + threshold crit, int icmp_sock, unsigned short number_of_targets, + check_icmp_state *program_state, ping_target *target_list); /* Error exit */ static void crash(const char * /*fmt*/, ...); /** global variables **/ -static struct rta_host *cursor = NULL; -static struct rta_host *host_list = NULL; - static int debug = 0; -/** the working code **/ +extern unsigned int timeout; +/** the working code **/ static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) { return targets - targets_down; } @@ -259,7 +265,8 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { } } - /* Parse protocol arguments first */ + // Parse protocol arguments first + // and count hosts here char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64"; for (int i = 1; i < argc; i++) { long int arg; @@ -281,10 +288,31 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { usage(_("IPv6 support not available\n")); #endif break; + case 'H': { + result.config.number_of_hosts++; + } } } } + char **tmp = &argv[optind]; + while (*tmp) { + result.config.number_of_hosts++; + tmp++; + } + + // Sanity check: if hostmode is selected,only a single host is allowed + if (result.config.mode == MODE_HOSTCHECK && result.config.number_of_hosts > 1) { + usage("check_host only allows a single host"); + } + + // Allocate hosts + result.config.hosts = + calloc(result.config.number_of_hosts, sizeof(check_icmp_target_container)); + if (result.config.hosts == NULL) { + crash("failed to allocate memory"); + } + /* Reset argument scanning */ optind = 1; @@ -301,8 +329,8 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { long size = strtol(optarg, NULL, 0); if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) { - result.config.icmp_data_size = size; - result.config.icmp_pkt_size = size + ICMP_MINLEN; + result.config.icmp_data_size = (unsigned short)size; + result.config.icmp_pkt_size = (unsigned short)(size + ICMP_MINLEN); } else { usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data), @@ -323,24 +351,26 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { break; case 'n': case 'p': - result.config.packets = strtoul(optarg, NULL, 0); + result.config.packets = (unsigned short)strtoul(optarg, NULL, 0); if (result.config.packets > 20) { errno = 0; crash("packets is > 20 (%d)", result.config.packets); } break; case 't': - result.config.timeout = strtoul(optarg, NULL, 0); + timeout = (unsigned int)strtoul(optarg, NULL, 0); // TODO die here and complain about wrong input - // instead of: - if (!result.config.timeout) { - result.config.timeout = 10; - } break; case 'H': { - int add_result = add_target(optarg, result.config.mode); - if (add_result == 0) { - result.config.number_of_targets++; + add_target_wrapper add_result = add_target(optarg, result.config.mode); + if (add_result.error_code == OK) { + if (result.config.targets != NULL) { + result.config.number_of_targets += + ping_target_list_append(result.config.targets, add_result.targets); + } else { + result.config.targets = add_result.targets; + result.config.number_of_targets += add_result.number_of_targets; + } } } break; case 'l': @@ -569,7 +599,7 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, unsigned int *pkt_interval, unsigned int *target_interval, - const pid_t pid, struct rta_host **table, unsigned short packets, + const pid_t pid, ping_target **table, unsigned short packets, const unsigned short number_of_targets, check_icmp_state *program_state) { struct icmp p; @@ -611,7 +641,7 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad } /* it is indeed a response for us */ - struct rta_host *host = table[ntohs(sent_icmp.icmp_seq) / packets]; + ping_target *host = table[ntohs(sent_icmp.icmp_seq) / packets]; if (debug) { char address[INET6_ADDRSTRLEN]; parse_address(addr, address, sizeof(address)); @@ -629,8 +659,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad /* source quench means we're sending too fast, so increase the * interval and mark this packet lost */ if (p.icmp_type == ICMP_SOURCEQUENCH) { - *pkt_interval *= PACKET_BACKOFF_FACTOR; - *target_interval *= TARGET_BACKOFF_FACTOR; + *pkt_interval = (unsigned int)(*pkt_interval * PACKET_BACKOFF_FACTOR); + *target_interval = (unsigned int)(*target_interval * TARGET_BACKOFF_FACTOR); } else { program_state->targets_down++; host->flags |= FLAG_LOST_CAUSE; @@ -642,7 +672,7 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad return 0; } -void parse_address(struct sockaddr_storage *addr, char *address, int size) { +void parse_address(struct sockaddr_storage *addr, char *address, socklen_t size) { switch (address_family) { case AF_INET: inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); @@ -722,32 +752,24 @@ int main(int argc, char **argv) { } } -#ifdef HAVE_SIGACTION struct sigaction sig_action; - sig_action.sa_sigaction = NULL; - sig_action.sa_handler = finish; + sig_action.sa_handler = NULL; + sig_action.sa_sigaction = check_icmp_timeout_handler; sigfillset(&sig_action.sa_mask); - sig_action.sa_flags = SA_NODEFER | SA_RESTART; + sig_action.sa_flags = SA_NODEFER | SA_RESTART | SA_SIGINFO; sigaction(SIGINT, &sig_action, NULL); sigaction(SIGHUP, &sig_action, NULL); sigaction(SIGTERM, &sig_action, NULL); sigaction(SIGALRM, &sig_action, NULL); -#else /* HAVE_SIGACTION */ - // signal(SIGINT, finish); - // signal(SIGHUP, finish); - // signal(SIGTERM, finish); - // signal(SIGALRM, finish); -#endif /* HAVE_SIGACTION */ if (debug) { - printf("Setting alarm timeout to %u seconds\n", config.timeout); + printf("Setting alarm timeout to %u seconds\n", timeout); } - alarm(config.timeout); + alarm(timeout); /* make sure we don't wait any longer than necessary */ - struct timezone time_zone_dummy; struct timeval prog_start; - gettimeofday(&prog_start, &time_zone_dummy); + gettimeofday(&prog_start, NULL); unsigned int max_completion_time = ((config.number_of_targets * config.packets * config.pkt_interval) + @@ -765,8 +787,8 @@ int main(int argc, char **argv) { } if (debug) { - if (max_completion_time > (unsigned int)config.timeout * 1000000) { - printf("max_completion_time: %u timeout: %u\n", max_completion_time, config.timeout); + if (max_completion_time > (timeout * 1000000)) { + printf("max_completion_time: %u timeout: %u\n", max_completion_time, timeout); printf("Timeout must be at least %u\n", (max_completion_time / 1000000) + 1); } } @@ -776,7 +798,7 @@ int main(int argc, char **argv) { config.warn.rta, config.warn.pl); printf("pkt_interval: %u target_interval: %u\n", config.pkt_interval, config.target_interval); - printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_pkt_size, config.timeout); + printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_pkt_size, timeout); } if (config.min_hosts_alive < -1) { @@ -784,18 +806,18 @@ int main(int argc, char **argv) { crash("minimum alive hosts is negative (%i)", config.min_hosts_alive); } - struct rta_host *host = host_list; - struct rta_host **table = malloc(sizeof(struct rta_host *) * config.number_of_targets); + ping_target *host = config.targets; + ping_target **table = malloc(sizeof(ping_target *) * config.number_of_targets); if (!table) { crash("main(): malloc failed for host table"); } - unsigned short i = 0; + unsigned short target_index = 0; while (host) { - host->id = i * config.packets; - table[i] = host; + host->id = target_index * config.packets; + table[target_index] = host; host = host->next; - i++; + target_index++; } unsigned int pkt_interval = config.pkt_interval; @@ -806,13 +828,13 @@ int main(int argc, char **argv) { run_checks(config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, config.jitter_mode, config.score_mode, config.min_hosts_alive, config.icmp_data_size, &pkt_interval, &target_interval, config.warn, config.crit, config.pid, config.mode, - max_completion_time, &prog_start, table, config.packets, icmp_sock, - config.number_of_targets, &program_state); + max_completion_time, prog_start, table, config.packets, icmp_sock, + config.number_of_targets, &program_state, config.targets); errno = 0; finish(0, config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, config.jitter_mode, config.score_mode, config.min_hosts_alive, config.warn, config.crit, - icmp_sock, config.number_of_targets, &program_state); + icmp_sock, config.number_of_targets, &program_state, config.targets); return (0); } @@ -822,9 +844,10 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo unsigned short icmp_pkt_size, unsigned int *pkt_interval, unsigned int *target_interval, threshold warn, threshold crit, const pid_t pid, const int mode, const unsigned int max_completion_time, - const struct timeval *prog_start, struct rta_host **table, + const struct timeval prog_start, ping_target **table, const unsigned short packets, const int icmp_sock, - const unsigned short number_of_targets, check_icmp_state *program_state) { + const unsigned short number_of_targets, check_icmp_state *program_state, + ping_target *target_list) { /* this loop might actually violate the pkt_interval or target_interval * settings, but only if there aren't any packets on the wire which * indicates that the target can handle an increased packet rate */ @@ -833,7 +856,8 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo /* don't send useless packets */ if (!targets_alive(number_of_targets, program_state->targets_down)) { finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, - min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state); + min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state, + target_list); } if (table[target_index]->flags & FLAG_LOST_CAUSE) { if (debug) { @@ -845,25 +869,32 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo /* we're still in the game, so send next packet */ (void)send_icmp_ping(icmp_sock, table[target_index], icmp_pkt_size, pid, program_state); - wait_for_reply(icmp_sock, *target_interval, order_mode, mos_mode, rta_mode, pl_mode, - jitter_mode, score_mode, min_hosts_alive, icmp_pkt_size, pkt_interval, - target_interval, warn, crit, pid, mode, max_completion_time, prog_start, - table, packets, icmp_sock, number_of_targets, program_state); + /* wrap up if all targets are declared dead */ + if (targets_alive(number_of_targets, program_state->targets_down) || + get_timevaldiff(prog_start, prog_start) < max_completion_time || + !(mode == MODE_HOSTCHECK && program_state->targets_down)) { + wait_for_reply(icmp_sock, *target_interval, icmp_pkt_size, pkt_interval, + target_interval, pid, table, packets, number_of_targets, + program_state); + } + } + if (targets_alive(number_of_targets, program_state->targets_down) || + get_timevaldiff_to_now(prog_start) < max_completion_time || + !(mode == MODE_HOSTCHECK && program_state->targets_down)) { + wait_for_reply(icmp_sock, *pkt_interval * number_of_targets, icmp_pkt_size, + pkt_interval, target_interval, pid, table, packets, number_of_targets, + program_state); } - wait_for_reply(icmp_sock, *pkt_interval * number_of_targets, order_mode, mos_mode, rta_mode, - pl_mode, jitter_mode, score_mode, min_hosts_alive, icmp_pkt_size, - pkt_interval, target_interval, warn, crit, pid, mode, max_completion_time, - prog_start, table, packets, icmp_sock, number_of_targets, program_state); } if (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv, program_state->icmp_lost) && targets_alive(number_of_targets, program_state->targets_down)) { - unsigned int time_passed = get_timevaldiff(NULL, NULL, prog_start); - unsigned int final_wait = max_completion_time - time_passed; + time_t time_passed = get_timevaldiff_to_now(prog_start); + time_t final_wait = max_completion_time - time_passed; if (debug) { - printf("time_passed: %u final_wait: %u max_completion_time: %u\n", time_passed, + printf("time_passed: %ld final_wait: %ld max_completion_time: %u\n", time_passed, final_wait, max_completion_time); } if (time_passed > max_completion_time) { @@ -871,19 +902,22 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo printf("Time passed. Finishing up\n"); } finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, - min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state); + min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state, + target_list); } /* catch the packets that might come in within the timeframe, but * haven't yet */ if (debug) { - printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, + printf("Waiting for %ld micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000); } - wait_for_reply(icmp_sock, final_wait, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, - score_mode, min_hosts_alive, icmp_pkt_size, pkt_interval, target_interval, - warn, crit, pid, mode, max_completion_time, prog_start, table, packets, - icmp_sock, number_of_targets, program_state); + if (targets_alive(number_of_targets, program_state->targets_down) || + get_timevaldiff_to_now(prog_start) < max_completion_time || + !(mode == MODE_HOSTCHECK && program_state->targets_down)) { + wait_for_reply(icmp_sock, final_wait, icmp_pkt_size, pkt_interval, target_interval, pid, + table, packets, number_of_targets, program_state); + } } } @@ -897,15 +931,10 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo * both: * icmp echo reply : the rest */ -static int wait_for_reply(int sock, const unsigned int time_interval, bool order_mode, - bool mos_mode, bool rta_mode, bool pl_mode, bool jitter_mode, - bool score_mode, int min_hosts_alive, unsigned short icmp_pkt_size, - unsigned int *pkt_interval, unsigned int *target_interval, threshold warn, - threshold crit, const pid_t pid, const int mode, - const unsigned long long max_completion_time, struct timeval *prog_start, - struct rta_host **table, const unsigned short packets, - const int icmp_sock, const unsigned short number_of_targets, - check_icmp_state *program_state) { +static int wait_for_reply(int sock, const time_t time_interval, unsigned short icmp_pkt_size, + unsigned int *pkt_interval, unsigned int *target_interval, pid_t pid, + ping_target **table, const unsigned short packets, + const unsigned short number_of_targets, check_icmp_state *program_state) { union icmp_packet packet; if (!(packet.buf = malloc(icmp_pkt_size))) { crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); @@ -921,56 +950,50 @@ static int wait_for_reply(int sock, const unsigned int time_interval, bool order return 0; } + // Get current time stamp struct timeval wait_start; - struct timezone time_zone_dummy; - gettimeofday(&wait_start, &time_zone_dummy); + gettimeofday(&wait_start, NULL); struct sockaddr_storage resp_addr; - unsigned int per_pkt_wait = + time_t per_pkt_wait = time_interval / icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv, program_state->icmp_lost); static unsigned char buf[65536]; - union ip_hdr *ip; - struct timeval now; + union ip_hdr *ip_header; + struct timeval packet_received_timestamp; while (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv, program_state->icmp_lost) && - get_timevaldiff(&wait_start, NULL, prog_start) < time_interval) { - unsigned int loop_time_interval = per_pkt_wait; - - /* wrap up if all targets are declared dead */ - if (!targets_alive(number_of_targets, program_state->targets_down) || - get_timevaldiff(prog_start, NULL, prog_start) >= max_completion_time || - (mode == MODE_HOSTCHECK && program_state->targets_down)) { - finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, - min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state); - } + get_timevaldiff_to_now(wait_start) < time_interval) { + time_t loop_time_interval = per_pkt_wait; /* reap responses until we hit a timeout */ - int n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, - &loop_time_interval, &now, prog_start); + ssize_t n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, + &loop_time_interval, &packet_received_timestamp); if (!n) { if (debug > 1) { - printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); + printf("recvfrom_wto() timed out during a %ld usecs wait\n", per_pkt_wait); } continue; /* timeout for this one, so keep trying */ } + if (n < 0) { if (debug) { printf("recvfrom_wto() returned errors\n"); } free(packet.buf); - return n; + return (int)n; } // FIXME: with ipv6 we don't have an ip header here if (address_family != AF_INET6) { - ip = (union ip_hdr *)buf; + ip_header = (union ip_hdr *)buf; if (debug > 1) { char address[INET6_ADDRSTRLEN]; parse_address(&resp_addr, address, sizeof(address)); printf("received %u bytes from %s\n", - address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), + address_family == AF_INET6 ? ntohs(ip_header->ip6.ip6_plen) + : ntohs(ip_header->ip.ip_len), address); } } @@ -982,7 +1005,7 @@ static int wait_for_reply(int sock, const unsigned int time_interval, bool order * off the bottom 4 bits */ /* hlen = (ip->ip_vhl & 0x0f) << 2; */ /* #else */ - int hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2; + int hlen = (address_family == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2; /* #endif */ if (n < (hlen + ICMP_MINLEN)) { @@ -1020,7 +1043,7 @@ static int wait_for_reply(int sock, const unsigned int time_interval, bool order } /* this is indeed a valid response */ - struct rta_host *host; + ping_target *target; struct icmp_ping_data data; if (address_family == PF_INET) { memcpy(&data, packet.icp->icmp_data, sizeof(data)); @@ -1029,7 +1052,7 @@ static int wait_for_reply(int sock, const unsigned int time_interval, bool order ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); } - host = table[ntohs(packet.icp->icmp_seq) / packets]; + target = table[ntohs(packet.icp->icmp_seq) / packets]; } else { memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); if (debug > 2) { @@ -1037,55 +1060,55 @@ static int wait_for_reply(int sock, const unsigned int time_interval, bool order ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); } - host = table[ntohs(packet.icp6->icmp6_seq) / packets]; + target = table[ntohs(packet.icp6->icmp6_seq) / packets]; } - unsigned int tdiff = get_timevaldiff(&data.stime, &now, prog_start); + time_t tdiff = get_timevaldiff(data.stime, packet_received_timestamp); - if (host->last_tdiff > 0) { + if (target->last_tdiff > 0) { /* Calculate jitter */ double jitter_tmp; - if (host->last_tdiff > tdiff) { - jitter_tmp = host->last_tdiff - tdiff; + if (target->last_tdiff > tdiff) { + jitter_tmp = (double)(target->last_tdiff - tdiff); } else { - jitter_tmp = tdiff - host->last_tdiff; + jitter_tmp = (double)(tdiff - target->last_tdiff); } - if (host->jitter == 0) { - host->jitter = jitter_tmp; - host->jitter_max = jitter_tmp; - host->jitter_min = jitter_tmp; + if (target->jitter == 0) { + target->jitter = jitter_tmp; + target->jitter_max = jitter_tmp; + target->jitter_min = jitter_tmp; } else { - host->jitter += jitter_tmp; + target->jitter += jitter_tmp; - if (jitter_tmp < host->jitter_min) { - host->jitter_min = jitter_tmp; + if (jitter_tmp < target->jitter_min) { + target->jitter_min = jitter_tmp; } - if (jitter_tmp > host->jitter_max) { - host->jitter_max = jitter_tmp; + if (jitter_tmp > target->jitter_max) { + target->jitter_max = jitter_tmp; } } /* Check if packets in order */ - if (host->last_icmp_seq >= packet.icp->icmp_seq) { - host->order_status = STATE_CRITICAL; + if (target->last_icmp_seq >= packet.icp->icmp_seq) { + target->order_status = STATE_CRITICAL; } } - host->last_tdiff = tdiff; + target->last_tdiff = tdiff; - host->last_icmp_seq = packet.icp->icmp_seq; + target->last_icmp_seq = packet.icp->icmp_seq; - host->time_waited += tdiff; - host->icmp_recv++; + target->time_waited += tdiff; + target->icmp_recv++; program_state->icmp_recv++; - if (tdiff > (unsigned int)host->rtmax) { - host->rtmax = tdiff; + if (tdiff > (unsigned int)target->rtmax) { + target->rtmax = (double)tdiff; } - if ((host->rtmin == INFINITY) || (tdiff < (unsigned int)host->rtmin)) { - host->rtmin = tdiff; + if ((target->rtmin == INFINITY) || (tdiff < (unsigned int)target->rtmin)) { + target->rtmin = (double)tdiff; } if (debug) { @@ -1095,26 +1118,16 @@ static int wait_for_reply(int sock, const unsigned int time_interval, bool order switch (address_family) { case AF_INET: { printf("%0.3f ms rtt from %s, incoming ttl: %u, max: %0.3f, min: %0.3f\n", - (float)tdiff / 1000, address, ip->ip.ip_ttl, (float)host->rtmax / 1000, - (float)host->rtmin / 1000); + (float)tdiff / 1000, address, ip_header->ip.ip_ttl, + (float)target->rtmax / 1000, (float)target->rtmin / 1000); break; }; case AF_INET6: { printf("%0.3f ms rtt from %s, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, - address, (float)host->rtmax / 1000, (float)host->rtmin / 1000); + address, (float)target->rtmax / 1000, (float)target->rtmin / 1000); }; } } - - /* if we're in hostcheck mode, exit with limited printouts */ - if (mode == MODE_HOSTCHECK) { - printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|" - "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n", - host->name, program_state->icmp_recv, (float)tdiff / 1000, - program_state->icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000, - (float)crit.rta / 1000); - exit(STATE_OK); - } } free(packet.buf); @@ -1122,7 +1135,7 @@ static int wait_for_reply(int sock, const unsigned int time_interval, bool order } /* the ping functions */ -static int send_icmp_ping(const int sock, struct rta_host *host, const unsigned short icmp_pkt_size, +static int send_icmp_ping(const int sock, ping_target *host, const unsigned short icmp_pkt_size, const pid_t pid, check_icmp_state *program_state) { if (sock == -1) { errno = 0; @@ -1140,18 +1153,17 @@ static int send_icmp_ping(const int sock, struct rta_host *host, const unsigned } memset(buf, 0, icmp_pkt_size); - struct timeval tv; - struct timezone tz; - if ((gettimeofday(&tv, &tz)) == -1) { + struct timeval current_time; + if ((gettimeofday(¤t_time, NULL)) == -1) { free(buf); return -1; } struct icmp_ping_data data; data.ping_id = 10; /* host->icmp.icmp_sent; */ - memcpy(&data.stime, &tv, sizeof(tv)); + memcpy(&data.stime, ¤t_time, sizeof(current_time)); - size_t addrlen; + socklen_t addrlen; if (address_family == AF_INET) { struct icmp *icp = (struct icmp *)buf; @@ -1162,7 +1174,7 @@ static int send_icmp_ping(const int sock, struct rta_host *host, const unsigned icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; icp->icmp_cksum = 0; - icp->icmp_id = htons(pid); + icp->icmp_id = htons((uint16_t)pid); icp->icmp_seq = htons(host->id++); icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); @@ -1180,7 +1192,7 @@ static int send_icmp_ping(const int sock, struct rta_host *host, const unsigned icp6->icmp6_type = ICMP6_ECHO_REQUEST; icp6->icmp6_code = 0; icp6->icmp6_cksum = 0; - icp6->icmp6_id = htons(pid); + icp6->icmp6_id = htons((uint16_t)pid); icp6->icmp6_seq = htons(host->id++); // let checksum be calculated automatically @@ -1231,8 +1243,9 @@ static int send_icmp_ping(const int sock, struct rta_host *host, const unsigned return 0; } -static int recvfrom_wto(const int sock, void *buf, const unsigned int len, struct sockaddr *saddr, - unsigned int *timeout, struct timeval *tv, struct timeval *prog_start) { +static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, + struct sockaddr *saddr, time_t *timeout, + struct timeval *received_timestamp) { #ifdef HAVE_MSGHDR_MSG_CONTROL char ans_data[4096]; #endif // HAVE_MSGHDR_MSG_CONTROL @@ -1247,31 +1260,33 @@ static int recvfrom_wto(const int sock, void *buf, const unsigned int len, struc return 0; } - struct timeval to; - to.tv_sec = *timeout / 1000000; - to.tv_usec = (*timeout - (to.tv_sec * 1000000)); + struct timeval real_timeout; + real_timeout.tv_sec = *timeout / 1000000; + real_timeout.tv_usec = (*timeout - (real_timeout.tv_sec * 1000000)); - fd_set rd; - fd_set wr; - FD_ZERO(&rd); - FD_ZERO(&wr); - FD_SET(sock, &rd); - errno = 0; + // Dummy fds for select + fd_set dummy_write_fds; + FD_ZERO(&dummy_write_fds); + + // Read fds for select with the socket + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(sock, &read_fds); struct timeval then; - struct timezone time_zone_dummy; - gettimeofday(&then, &time_zone_dummy); + gettimeofday(&then, NULL); - int n = select(sock + 1, &rd, &wr, NULL, &to); - if (n < 0) { + errno = 0; + int select_return = select(sock + 1, &read_fds, &dummy_write_fds, NULL, &real_timeout); + if (select_return < 0) { crash("select() in recvfrom_wto"); } struct timeval now; - gettimeofday(&now, &time_zone_dummy); - *timeout = get_timevaldiff(&then, &now, prog_start); + gettimeofday(&now, NULL); + *timeout = get_timevaldiff(then, now); - if (!n) { + if (!select_return) { return 0; /* timeout */ } @@ -1299,16 +1314,16 @@ static int recvfrom_wto(const int sock, void *buf, const unsigned int len, struc for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { - memcpy(tv, CMSG_DATA(chdr), sizeof(*tv)); + memcpy(received_timestamp, CMSG_DATA(chdr), sizeof(*received_timestamp)); break; } } if (!chdr) { - gettimeofday(tv, &time_zone_dummy); + gettimeofday(received_timestamp, NULL); } #else - gettimeofday(tv, &time_zone_dummy); + gettimeofday(tv, NULL); #endif // SO_TIMESTAMP return (ret); @@ -1317,7 +1332,7 @@ static int recvfrom_wto(const int sock, void *buf, const unsigned int len, struc static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, bool jitter_mode, bool score_mode, int min_hosts_alive, threshold warn, threshold crit, const int icmp_sock, const unsigned short number_of_targets, - check_icmp_state *program_state) { + check_icmp_state *program_state, ping_target *target_list) { // Deactivate alarm alarm(0); @@ -1338,7 +1353,7 @@ static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool /* iterate thrice to calculate values, give output, and print perfparse */ mp_state_enum status = STATE_OK; - struct rta_host *host = host_list; + ping_target *host = target_list; unsigned int target_counter = 0; const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"}; @@ -1361,7 +1376,8 @@ static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool program_state->targets_down++; } } else { - packet_loss = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent; + packet_loss = + (unsigned char)((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent; rta = (double)host->time_waited / host->icmp_recv; } @@ -1507,7 +1523,7 @@ static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool } printf("%s - ", status_string[status]); - host = host_list; + host = target_list; while (host) { if (debug) { puts(""); @@ -1611,8 +1627,9 @@ static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) { printf("|"); } + target_counter = 0; - host = host_list; + host = target_list; while (host) { if (debug) { puts(""); @@ -1696,32 +1713,27 @@ static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool exit(status); } -static time_t get_timevaldiff(struct timeval *earlier, struct timeval *later, - struct timeval *prog_start) { - struct timeval now; - - if (!later) { - struct timezone time_zone_dummy; - gettimeofday(&now, &time_zone_dummy); - later = &now; - } - if (!earlier) { - earlier = prog_start; - } - +static time_t get_timevaldiff(const struct timeval earlier, const struct timeval later) { /* if early > later we return 0 so as to indicate a timeout */ - if (earlier->tv_sec > later->tv_sec || - (earlier->tv_sec == later->tv_sec && earlier->tv_usec > later->tv_usec)) { + if (earlier.tv_sec > later.tv_sec || + (earlier.tv_sec == later.tv_sec && earlier.tv_usec > later.tv_usec)) { return 0; } - time_t ret = (later->tv_sec - earlier->tv_sec) * 1000000; - ret += later->tv_usec - earlier->tv_usec; + time_t ret = (later.tv_sec - earlier.tv_sec) * 1000000; + ret += later.tv_usec - earlier.tv_usec; return ret; } -static int add_target_ip(char *arg, struct sockaddr_storage *address) { +static time_t get_timevaldiff_to_now(struct timeval earlier) { + struct timeval now; + gettimeofday(&now, NULL); + + return get_timevaldiff(earlier, now); +} + +static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *address) { struct sockaddr_in *sin; struct sockaddr_in6 *sin6; if (address_family == AF_INET) { @@ -1730,110 +1742,127 @@ static int add_target_ip(char *arg, struct sockaddr_storage *address) { sin6 = (struct sockaddr_in6 *)address; } + add_target_ip_wrapper result = { + .error_code = OK, + .target = NULL, + }; + /* disregard obviously stupid addresses * (I didn't find an ipv6 equivalent to INADDR_NONE) */ if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { - return -1; + result.error_code = ERROR; + return result; } + // TODO: allow duplicate targets for now, might be on purpose /* no point in adding two identical IP's, so don't. ;) */ - struct sockaddr_in *host_sin; - struct sockaddr_in6 *host_sin6; - struct rta_host *host = host_list; - while (host) { - host_sin = (struct sockaddr_in *)&host->saddr_in; - host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; - - if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) || - (address_family == AF_INET6 && - host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { - if (debug) { - printf("Identical IP already exists. Not adding %s\n", arg); - } - return -1; - } - host = host->next; - } + // struct sockaddr_in *host_sin; + // struct sockaddr_in6 *host_sin6; + // ping_target *host = host_list; + // while (host) { + // host_sin = (struct sockaddr_in *)&host->saddr_in; + // host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; + + // if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) || + // (address_family == AF_INET6 && + // host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { + // if (debug) { + // printf("Identical IP already exists. Not adding %s\n", arg); + // } + // return -1; + // } + // host = host->next; + // } /* add the fresh ip */ - host = (struct rta_host *)malloc(sizeof(struct rta_host)); - if (!host) { + ping_target *target = (ping_target *)malloc(sizeof(ping_target)); + if (!target) { char straddr[INET6_ADDRSTRLEN]; parse_address((struct sockaddr_storage *)&address, straddr, sizeof(straddr)); - crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host)); + crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(ping_target)); } - *host = ping_target_init(); + *target = ping_target_init(); /* set the values. use calling name for output */ - host->name = strdup(arg); + target->name = strdup(arg); /* fill out the sockaddr_storage struct */ + struct sockaddr_in *host_sin; + struct sockaddr_in6 *host_sin6; if (address_family == AF_INET) { - host_sin = (struct sockaddr_in *)&host->saddr_in; + host_sin = (struct sockaddr_in *)&target->saddr_in; host_sin->sin_family = AF_INET; host_sin->sin_addr.s_addr = sin->sin_addr.s_addr; } else { - host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; + host_sin6 = (struct sockaddr_in6 *)&target->saddr_in; host_sin6->sin6_family = AF_INET6; memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr); } - if (!host_list) { - host_list = cursor = host; - } else { - cursor->next = host; - } - - cursor = host; + result.target = target; - return 0; + return result; } /* wrapper for add_target_ip */ -static int add_target(char *arg, const int mode) { - struct sockaddr_storage ip; +static add_target_wrapper add_target(char *arg, const int mode) { + struct sockaddr_storage address_storage; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; - int result = -1; + int error_code = -1; switch (address_family) { case -1: /* -4 and -6 are not specified on cmdline */ address_family = AF_INET; - sin = (struct sockaddr_in *)&ip; - result = inet_pton(address_family, arg, &sin->sin_addr); + sin = (struct sockaddr_in *)&address_storage; + error_code = inet_pton(address_family, arg, &sin->sin_addr); #ifdef USE_IPV6 - if (result != 1) { + if (error_code != 1) { address_family = AF_INET6; - sin6 = (struct sockaddr_in6 *)&ip; - result = inet_pton(address_family, arg, &sin6->sin6_addr); + sin6 = (struct sockaddr_in6 *)&address_storage; + error_code = inet_pton(address_family, arg, &sin6->sin6_addr); } #endif /* If we don't find any valid addresses, we still don't know the address_family */ - if (result != 1) { + if (error_code != 1) { address_family = -1; } break; case AF_INET: - sin = (struct sockaddr_in *)&ip; - result = inet_pton(address_family, arg, &sin->sin_addr); + sin = (struct sockaddr_in *)&address_storage; + error_code = inet_pton(address_family, arg, &sin->sin_addr); break; case AF_INET6: - sin6 = (struct sockaddr_in6 *)&ip; - result = inet_pton(address_family, arg, &sin6->sin6_addr); + sin6 = (struct sockaddr_in6 *)&address_storage; + error_code = inet_pton(address_family, arg, &sin6->sin6_addr); break; default: crash("Address family not supported"); } + add_target_wrapper result = { + .error_code = OK, + .targets = NULL, + }; + /* don't resolve if we don't have to */ - if (result == 1) { + if (error_code == 1) { /* don't add all ip's if we were given a specific one */ - return add_target_ip(arg, &ip); + add_target_ip_wrapper targeted = add_target_ip(arg, &address_storage); + + if (targeted.error_code != OK) { + result.error_code = ERROR; + return result; + } + + result.targets = targeted.target; + result.number_of_targets = 1; + return result; } struct addrinfo hints; @@ -1851,14 +1880,28 @@ static int add_target(char *arg, const int mode) { if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { errno = 0; crash("Failed to resolve %s: %s", arg, gai_strerror(error)); - return -1; + result.error_code = ERROR; + return result; } address_family = res->ai_family; /* possibly add all the IP's as targets */ - for (struct addrinfo *p = res; p != NULL; p = p->ai_next) { - memcpy(&ip, p->ai_addr, p->ai_addrlen); - add_target_ip(arg, &ip); + for (struct addrinfo *address = res; address != NULL; address = address->ai_next) { + struct sockaddr_storage temporary_ip_address; + memcpy(&temporary_ip_address, address->ai_addr, address->ai_addrlen); + add_target_ip_wrapper tmp = add_target_ip(arg, &temporary_ip_address); + + if (tmp.error_code != OK) { + // No proper error handling + // What to do? + } else { + if (result.targets == NULL) { + result.targets = tmp.target; + result.number_of_targets = 1; + } else { + result.number_of_targets += ping_target_list_append(result.targets, tmp.target); + } + } /* this is silly, but it works */ if (mode == MODE_HOSTCHECK || mode == MODE_ALL) { @@ -1867,11 +1910,13 @@ static int add_target(char *arg, const int mode) { } continue; } + + // Abort after first hit if not in of the modes above break; } freeaddrinfo(res); - return 0; + return result; } static void set_source_ip(char *arg, const int icmp_sock) { @@ -1890,8 +1935,8 @@ static void set_source_ip(char *arg, const int icmp_sock) { /* TODO: Move this to netutils.c and also change check_dhcp to use that. */ static in_addr_t get_ip_address(const char *ifname) { // TODO: Rewrite this so the function return an error and we exit somewhere else - struct sockaddr_in ip; - ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy + struct sockaddr_in ip_address; + ip_address.sin_addr.s_addr = 0; // Fake initialization to make compiler happy #if defined(SIOCGIFADDR) struct ifreq ifr; @@ -1909,7 +1954,7 @@ static in_addr_t get_ip_address(const char *ifname) { errno = 0; crash("Cannot get interface IP address on this platform."); #endif - return ip.sin_addr.s_addr; + return ip_address.sin_addr.s_addr; } /* @@ -1930,87 +1975,90 @@ static unsigned int get_timevar(const char *str) { /* unit might be given as ms|m (millisec), * us|u (microsec) or just plain s, for seconds */ - char p = '\0'; - char u = str[len - 1]; + char tmp = '\0'; + char unit = str[len - 1]; if (len >= 2 && !isdigit((int)str[len - 2])) { - p = str[len - 2]; + tmp = str[len - 2]; } - if (p && u == 's') { - u = p; - } else if (!p) { - p = u; + + if (tmp && unit == 's') { + unit = tmp; + } else if (!tmp) { + tmp = unit; } + if (debug > 2) { - printf("evaluating %s, u: %c, p: %c\n", str, u, p); + printf("evaluating %s, u: %c, p: %c\n", str, unit, tmp); } unsigned int factor = 1000; /* default to milliseconds */ - if (u == 'u') { + if (unit == 'u') { factor = 1; /* microseconds */ - } else if (u == 'm') { + } else if (unit == 'm') { factor = 1000; /* milliseconds */ - } else if (u == 's') { + } else if (unit == 's') { factor = 1000000; /* seconds */ } + if (debug > 2) { printf("factor is %u\n", factor); } char *ptr; - unsigned int i; - i = strtoul(str, &ptr, 0); + unsigned long pre_radix; + pre_radix = strtoul(str, &ptr, 0); if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { - return i * factor; + return (unsigned int)(pre_radix * factor); } /* time specified in usecs can't have decimal points, so ignore them */ if (factor == 1) { - return i; + return (unsigned int)pre_radix; } /* integer and decimal, respectively */ - unsigned int d = strtoul(ptr + 1, NULL, 0); + unsigned int post_radix = (unsigned int)strtoul(ptr + 1, NULL, 0); /* d is decimal, so get rid of excess digits */ - while (d >= factor) { - d /= 10; + while (post_radix >= factor) { + post_radix /= 10; } /* the last parenthesis avoids floating point exceptions. */ - return ((i * factor) + (d * (factor / 10))); + return (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10))); } /* not too good at checking errors, but it'll do (main() should barfe on -1) */ -static int get_threshold(char *str, threshold *th) { - if (!str || !strlen(str) || !th) { +static int get_threshold(char *str, threshold *threshold) { + if (!str || !strlen(str) || !threshold) { return -1; } /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ - char i = 0; - char *p = &str[strlen(str) - 1]; - while (p != &str[1]) { - if (*p == '%') { - *p = '\0'; - } else if (*p == ',' && i) { - *p = '\0'; /* reset it so get_timevar(str) works nicely later */ - th->pl = (unsigned char)strtoul(p + 1, NULL, 0); + bool is_at_last_char = false; + char *tmp = &str[strlen(str) - 1]; + while (tmp != &str[1]) { + if (*tmp == '%') { + *tmp = '\0'; + } else if (*tmp == ',' && is_at_last_char) { + *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */ + threshold->pl = (unsigned char)strtoul(tmp + 1, NULL, 0); break; } - i = 1; - p--; + is_at_last_char = true; + tmp--; } - th->rta = get_timevar(str); + threshold->rta = get_timevar(str); - if (!th->rta) { + if (!threshold->rta) { return -1; } - if (th->rta > MAXTTL * 1000000) { - th->rta = MAXTTL * 1000000; + if (threshold->rta > MAXTTL * 1000000) { + threshold->rta = MAXTTL * 1000000; } - if (th->pl > 100) { - th->pl = 100; + if (threshold->pl > 100) { + threshold->pl = 100; } return 0; @@ -2034,58 +2082,58 @@ static bool get_threshold2(char *str, size_t length, threshold *warn, threshold } // p points to the last char in str - char *p = &str[length - 1]; + char *work_pointer = &str[length - 1]; // first_iteration is bof-stop on stupid libc's bool first_iteration = true; - while (p != &str[0]) { - if ((*p == 'm') || (*p == '%')) { - *p = '\0'; - } else if (*p == ',' && !first_iteration) { - *p = '\0'; /* reset it so get_timevar(str) works nicely later */ + while (work_pointer != &str[0]) { + if ((*work_pointer == 'm') || (*work_pointer == '%')) { + *work_pointer = '\0'; + } else if (*work_pointer == ',' && !first_iteration) { + *work_pointer = '\0'; /* reset it so get_timevar(str) works nicely later */ - char *start_of_value = p + 1; + char *start_of_value = work_pointer + 1; if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) { return false; } } first_iteration = false; - p--; + work_pointer--; } - return parse_threshold2_helper(p, strlen(p), warn, mode); + return parse_threshold2_helper(work_pointer, strlen(work_pointer), warn, mode); } -static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) { +static bool parse_threshold2_helper(char *threshold_string, size_t length, threshold *thr, + threshold_mode mode) { char *resultChecker = {0}; switch (mode) { case const_rta_mode: - thr->rta = strtod(s, &resultChecker) * 1000; + thr->rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000); break; case const_packet_loss_mode: - thr->pl = (unsigned char)strtoul(s, &resultChecker, 0); + thr->pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0); break; case const_jitter_mode: - thr->jitter = strtod(s, &resultChecker); - + thr->jitter = strtod(threshold_string, &resultChecker); break; case const_mos_mode: - thr->mos = strtod(s, &resultChecker); + thr->mos = strtod(threshold_string, &resultChecker); break; case const_score_mode: - thr->score = strtod(s, &resultChecker); + thr->score = strtod(threshold_string, &resultChecker); break; } - if (resultChecker == s) { + if (resultChecker == threshold_string) { // Failed to parse return false; } - if (resultChecker != (s + length)) { + if (resultChecker != (threshold_string + length)) { // Trailing symbols return false; } @@ -2093,24 +2141,24 @@ static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, thre return true; } -unsigned short icmp_checksum(uint16_t *p, size_t n) { +unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) { long sum = 0; /* sizeof(uint16_t) == 2 */ - while (n >= 2) { - sum += *(p++); - n -= 2; + while (packet_size >= 2) { + sum += *(packet++); + packet_size -= 2; } /* mop up the occasional odd byte */ - if (n == 1) { - sum += *((uint8_t *)p - 1); + if (packet_size == 1) { + sum += *((uint8_t *)packet - 1); } sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ unsigned short cksum; - cksum = ~sum; /* ones-complement, trunc to 16 bits */ + cksum = (unsigned short)~sum; /* ones-complement, trunc to 16 bits */ return cksum; } diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 8f6d7362..2efe6e59 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -5,6 +5,9 @@ #include "./check_icmp_helpers.h" #include "../../plugins/netutils.h" +// timeout as a global variable to make it available to the timeout handler +unsigned int timeout = DEFAULT_TIMEOUT; + check_icmp_config check_icmp_config_init() { check_icmp_config tmp = { .source_ip = NULL, @@ -33,11 +36,14 @@ check_icmp_config check_icmp_config_init() { .score = 80.0}, .pid = {}, .mode = MODE_RTA, - .timeout = DEFAULT_TIMEOUT, .ttl = DEFAULT_TTL, .packets = DEFAULT_NUMBER_OF_PACKETS, + .number_of_targets = 0, + .targets = NULL, + + .number_of_hosts = 0, .hosts = NULL, }; return tmp; @@ -140,3 +146,43 @@ check_icmp_target_container check_icmp_target_container_init() { }; return tmp; } + +unsigned int ping_target_list_append(ping_target *list, ping_target *elem) { + if (elem == NULL || list == NULL) { + return 0; + } + + while (list->next != NULL) { + list = list->next; + } + + list->next = elem; + + unsigned int result = 1; + + while (elem->next != NULL) { + result++; + elem = elem->next; + } + + return result; +} + +void check_icmp_timeout_handler(int signal, siginfo_t * info, void *ucontext) { + // Ignore unused arguments + (void) info; + (void) ucontext; + mp_subcheck timeout_sc = mp_subcheck_init(); + timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state); + + if (signal == SIGALRM) { + xasprintf(&timeout_sc.output, _("timeout after %d seconds\n"), timeout); + } else { + xasprintf(&timeout_sc.output, _("timeout after %d seconds\n"), timeout); + } + + mp_check overall = mp_check_init(); + mp_add_subcheck_to_check(&overall, timeout_sc); + + mp_exit(overall); +} diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h index 49f720ec..7e8a4d9f 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.h +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -15,7 +15,7 @@ typedef struct rta_host { char *msg; /* icmp error message, if any */ struct sockaddr_storage saddr_in; /* the address of this host */ struct sockaddr_storage error_addr; /* stores address of error replies */ - unsigned long long time_waited; /* total time waited, in usecs */ + time_t time_waited; /* total time waited, in usecs */ unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ unsigned char icmp_type, icmp_code; /* type and code from errors */ unsigned short flags; /* control/status flags */ @@ -32,7 +32,7 @@ typedef struct rta_host { double mos; /* Mean opinion score */ double score; /* score */ - unsigned int last_tdiff; + time_t last_tdiff; unsigned int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */ unsigned char pl; /* measured packet loss */ @@ -71,3 +71,6 @@ typedef struct { } rta_host_create_wrapper; rta_host_create_wrapper rta_host_create(char *name, struct sockaddr_storage *address); +unsigned int ping_target_list_append(ping_target *list, ping_target *elem); + +void check_icmp_timeout_handler(int, siginfo_t *, void *); diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h new file mode 100644 index 00000000..deae9bec --- /dev/null +++ b/plugins-root/check_icmp.d/config.h @@ -0,0 +1,102 @@ +#pragma once + +#include "../../config.h" +#include "../../lib/states.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "./check_icmp_helpers.h" + +/* threshold structure. all values are maximum allowed, exclusive */ +typedef struct threshold { + unsigned char pl; /* max allowed packet loss in percent */ + unsigned int rta; /* roundtrip time average, microseconds */ + double jitter; /* jitter time average, microseconds */ + double mos; /* MOS */ + double score; /* Score */ +} threshold; + +typedef struct { + char *source_ip; + + bool order_mode; + bool mos_mode; + bool rta_mode; + bool pl_mode; + bool jitter_mode; + bool score_mode; + + int min_hosts_alive; + unsigned short icmp_data_size; + unsigned short icmp_pkt_size; + unsigned int pkt_interval; + unsigned int target_interval; + threshold crit; + threshold warn; + pid_t pid; + + int mode; + unsigned long ttl; + + unsigned short packets; + + unsigned short number_of_targets; + ping_target *targets; + + unsigned short number_of_hosts; + check_icmp_target_container *hosts; +} check_icmp_config; + +check_icmp_config check_icmp_config_init(); + +/* the data structure */ +typedef struct icmp_ping_data { + struct timeval stime; /* timestamp (saved in protocol struct as well) */ + unsigned short ping_id; +} icmp_ping_data; + +#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */ +#define IP_HDR_SIZE 20 +#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN) +#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data) +#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44) + +/* 80 msec packet interval by default */ +#define DEFAULT_PKT_INTERVAL 80000 +#define DEFAULT_TARGET_INTERVAL 0 + +#define DEFAULT_WARN_RTA 200000 +#define DEFAULT_CRIT_RTA 500000 +#define DEFAULT_WARN_PL 40 +#define DEFAULT_CRIT_PL 80 + +#define DEFAULT_TIMEOUT 10 +#define DEFAULT_TTL 64 + +/* the different modes of this program are as follows: + * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping) + * MODE_HOSTCHECK: Return immediately upon any sign of life + * In addition, sends packets to ALL addresses assigned + * to this host (as returned by gethostbyname() or + * gethostbyaddr() and expects one host only to be checked at + * a time. Therefore, any packet response what so ever will + * count as a sign of life, even when received outside + * crit.rta limit. Do not misspell any additional IP's. + * MODE_ALL: Requires packets from ALL requested IP to return OK (default). + * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without + * tcp and udp args does this) + */ +#define MODE_RTA 0 +#define MODE_HOSTCHECK 1 +#define MODE_ALL 2 +#define MODE_ICMP 3 + +#define DEFAULT_NUMBER_OF_PACKETS 5 + +#define PACKET_BACKOFF_FACTOR 1.5 +#define TARGET_BACKOFF_FACTOR 1.5 -- cgit v1.2.3-74-g34f1 From 5fd8191a50df6be712c9143ca6d73de7878f57d1 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 5 May 2025 22:52:56 +0200 Subject: WIP - check_icmp refactor 3 --- plugins-root/check_icmp.c | 150 ++++++++++++------------- plugins-root/check_icmp.d/check_icmp_helpers.c | 25 +++-- plugins-root/check_icmp.d/config.h | 58 +++++----- 3 files changed, 118 insertions(+), 115 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 99414014..9d163678 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -142,37 +142,38 @@ static void set_source_ip(char * /*arg*/, int icmp_sock); /* Receiving data */ static int wait_for_reply(int socket, time_t time_interval, unsigned short icmp_pkt_size, - unsigned int *pkt_interval, unsigned int *target_interval, pid_t pid, - ping_target **table, unsigned short packets, + unsigned int *pkt_interval, unsigned int *target_interval, + uint16_t sender_id, ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); static ssize_t recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, struct sockaddr * /*saddr*/, time_t *timeout, struct timeval * /*tv*/); static int handle_random_icmp(unsigned char * /*packet*/, struct sockaddr_storage * /*addr*/, - unsigned int *pkt_interval, unsigned int *target_interval, pid_t pid, + unsigned int *pkt_interval, unsigned int *target_interval, uint16_t sender_id, ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); /* Sending data */ static int send_icmp_ping(int /*sock*/, ping_target * /*host*/, unsigned short icmp_pkt_size, - pid_t pid, check_icmp_state *program_state); + uint16_t sender_id, check_icmp_state *program_state); /* Threshold related */ -static int get_threshold(char *str, threshold *threshold); -static bool get_threshold2(char *str, size_t length, threshold * /*warn*/, threshold * /*crit*/, - threshold_mode mode); -static bool parse_threshold2_helper(char *threshold_string, size_t length, threshold *thr, - threshold_mode mode); +static int get_threshold(char *str, check_icmp_threshold *threshold); +static bool get_threshold2(char *str, size_t length, check_icmp_threshold * /*warn*/, + check_icmp_threshold * /*crit*/, threshold_mode mode); +static bool parse_threshold2_helper(char *threshold_string, size_t length, + check_icmp_threshold *thr, threshold_mode mode); /* main test function */ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, bool jitter_mode, bool score_mode, int min_hosts_alive, unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, threshold warn, threshold crit, pid_t pid, - int mode, unsigned int max_completion_time, struct timeval prog_start, - ping_target **table, unsigned short packets, int icmp_sock, - unsigned short number_of_targets, check_icmp_state *program_state, - ping_target *target_list); + unsigned int *target_interval, check_icmp_threshold warn, + check_icmp_threshold crit, uint16_t sender_id, + check_icmp_execution_mode mode, unsigned int max_completion_time, + struct timeval prog_start, ping_target **table, unsigned short packets, + int icmp_sock, unsigned short number_of_targets, + check_icmp_state *program_state, ping_target *target_list); /* Target aquisition */ typedef struct { @@ -190,13 +191,14 @@ static add_target_ip_wrapper add_target_ip(char * /*arg*/, struct sockaddr_stora static void parse_address(struct sockaddr_storage * /*addr*/, char * /*address*/, socklen_t size); -static unsigned short icmp_checksum(uint16_t * /*p*/, size_t /*n*/); +static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size); /* End of run function */ static void finish(int /*sig*/, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, - bool jitter_mode, bool score_mode, int min_hosts_alive, threshold warn, - threshold crit, int icmp_sock, unsigned short number_of_targets, - check_icmp_state *program_state, ping_target *target_list); + bool jitter_mode, bool score_mode, int min_hosts_alive, + check_icmp_threshold warn, check_icmp_threshold crit, int icmp_sock, + unsigned short number_of_targets, check_icmp_state *program_state, + ping_target *target_list); /* Error exit */ static void crash(const char * /*fmt*/, ...); @@ -239,21 +241,21 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { /* use the pid to mark packets as ours */ /* Some systems have 32-bit pid_t so mask off only 16 bits */ - result.config.pid = getpid() & 0xffff; + result.config.sender_id = getpid() & 0xffff; if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { result.config.mode = MODE_ICMP; } else if (!strcmp(progname, "check_host")) { result.config.mode = MODE_HOSTCHECK; result.config.pkt_interval = 1000000; - result.config.packets = 5; + result.config.number_of_packets = 5; result.config.crit.rta = result.config.warn.rta = 1000000; result.config.crit.pl = result.config.warn.pl = 100; } else if (!strcmp(progname, "check_rta_multi")) { result.config.mode = MODE_ALL; result.config.target_interval = 0; result.config.pkt_interval = 50000; - result.config.packets = 5; + result.config.number_of_packets = 5; } /* support "--help" and "--version" */ if (argc == 2) { @@ -279,14 +281,10 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { address_family = AF_INET; break; case '6': -#ifdef USE_IPV6 if (address_family != -1) { crash("Multiple protocol versions not supported"); } address_family = AF_INET6; -#else - usage(_("IPv6 support not available\n")); -#endif break; case 'H': { result.config.number_of_hosts++; @@ -351,10 +349,10 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { break; case 'n': case 'p': - result.config.packets = (unsigned short)strtoul(optarg, NULL, 0); - if (result.config.packets > 20) { + result.config.number_of_packets = (unsigned short)strtoul(optarg, NULL, 0); + if (result.config.number_of_packets > 20) { errno = 0; - crash("packets is > 20 (%d)", result.config.packets); + crash("packets is > 20 (%d)", result.config.number_of_packets); } break; case 't': @@ -599,18 +597,18 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, unsigned int *pkt_interval, unsigned int *target_interval, - const pid_t pid, ping_target **table, unsigned short packets, + const uint16_t sender_id, ping_target **table, unsigned short packets, const unsigned short number_of_targets, check_icmp_state *program_state) { - struct icmp p; - memcpy(&p, packet, sizeof(p)); - if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { + struct icmp icmp_packet; + memcpy(&icmp_packet, packet, sizeof(icmp_packet)); + if (icmp_packet.icmp_type == ICMP_ECHO && ntohs(icmp_packet.icmp_id) == sender_id) { /* echo request from us to us (pinging localhost) */ return 0; } if (debug) { - printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr); + printf("handle_random_icmp(%p, %p)\n", (void *)&icmp_packet, (void *)addr); } /* only handle a few types, since others can't possibly be replies to @@ -623,8 +621,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad * TIMXCEED actually sends a proper icmp response we will have passed * too many hops to have a hope of reaching it later, in which case it * indicates overconfidence in the network, poor routing or both. */ - if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && - p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { + if (icmp_packet.icmp_type != ICMP_UNREACH && icmp_packet.icmp_type != ICMP_TIMXCEED && + icmp_packet.icmp_type != ICMP_SOURCEQUENCH && icmp_packet.icmp_type != ICMP_PARAMPROB) { return 0; } @@ -632,7 +630,7 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad * to RFC 792). If it isn't, just ignore it */ struct icmp sent_icmp; memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); - if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || + if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != sender_id || ntohs(sent_icmp.icmp_seq) >= number_of_targets * packets) { if (debug) { printf("Packet is no response to a packet we sent\n"); @@ -646,7 +644,7 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad char address[INET6_ADDRSTRLEN]; parse_address(addr, address, sizeof(address)); printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", - get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name); + get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address, host->name); } program_state->icmp_lost++; @@ -658,15 +656,15 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad /* source quench means we're sending too fast, so increase the * interval and mark this packet lost */ - if (p.icmp_type == ICMP_SOURCEQUENCH) { + if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) { *pkt_interval = (unsigned int)(*pkt_interval * PACKET_BACKOFF_FACTOR); *target_interval = (unsigned int)(*target_interval * TARGET_BACKOFF_FACTOR); } else { program_state->targets_down++; host->flags |= FLAG_LOST_CAUSE; } - host->icmp_type = p.icmp_type; - host->icmp_code = p.icmp_code; + host->icmp_type = icmp_packet.icmp_type; + host->icmp_code = icmp_packet.icmp_code; host->error_addr = *addr; return 0; @@ -772,18 +770,18 @@ int main(int argc, char **argv) { gettimeofday(&prog_start, NULL); unsigned int max_completion_time = - ((config.number_of_targets * config.packets * config.pkt_interval) + + ((config.number_of_targets * config.number_of_packets * config.pkt_interval) + (config.number_of_targets * config.target_interval)) + - (config.number_of_targets * config.packets * config.crit.rta) + config.crit.rta; + (config.number_of_targets * config.number_of_packets * config.crit.rta) + config.crit.rta; if (debug) { printf("packets: %u, targets: %u\n" "target_interval: %0.3f, pkt_interval %0.3f\n" "crit.rta: %0.3f\n" "max_completion_time: %0.3f\n", - config.packets, config.number_of_targets, (float)config.target_interval / 1000, - (float)config.pkt_interval / 1000, (float)config.crit.rta / 1000, - (float)max_completion_time / 1000); + config.number_of_packets, config.number_of_targets, + (float)config.target_interval / 1000, (float)config.pkt_interval / 1000, + (float)config.crit.rta / 1000, (float)max_completion_time / 1000); } if (debug) { @@ -814,7 +812,7 @@ int main(int argc, char **argv) { unsigned short target_index = 0; while (host) { - host->id = target_index * config.packets; + host->id = target_index * config.number_of_packets; table[target_index] = host; host = host->next; target_index++; @@ -827,9 +825,9 @@ int main(int argc, char **argv) { run_checks(config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, config.jitter_mode, config.score_mode, config.min_hosts_alive, config.icmp_data_size, - &pkt_interval, &target_interval, config.warn, config.crit, config.pid, config.mode, - max_completion_time, prog_start, table, config.packets, icmp_sock, - config.number_of_targets, &program_state, config.targets); + &pkt_interval, &target_interval, config.warn, config.crit, config.sender_id, + config.mode, max_completion_time, prog_start, table, config.number_of_packets, + icmp_sock, config.number_of_targets, &program_state, config.targets); errno = 0; finish(0, config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, @@ -842,8 +840,9 @@ int main(int argc, char **argv) { static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, bool jitter_mode, bool score_mode, int min_hosts_alive, unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, threshold warn, threshold crit, - const pid_t pid, const int mode, const unsigned int max_completion_time, + unsigned int *target_interval, check_icmp_threshold warn, + check_icmp_threshold crit, const uint16_t sender_id, + const check_icmp_execution_mode mode, const unsigned int max_completion_time, const struct timeval prog_start, ping_target **table, const unsigned short packets, const int icmp_sock, const unsigned short number_of_targets, check_icmp_state *program_state, @@ -867,14 +866,15 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo } /* we're still in the game, so send next packet */ - (void)send_icmp_ping(icmp_sock, table[target_index], icmp_pkt_size, pid, program_state); + (void)send_icmp_ping(icmp_sock, table[target_index], icmp_pkt_size, sender_id, + program_state); /* wrap up if all targets are declared dead */ if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff(prog_start, prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { wait_for_reply(icmp_sock, *target_interval, icmp_pkt_size, pkt_interval, - target_interval, pid, table, packets, number_of_targets, + target_interval, sender_id, table, packets, number_of_targets, program_state); } } @@ -882,8 +882,8 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo get_timevaldiff_to_now(prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { wait_for_reply(icmp_sock, *pkt_interval * number_of_targets, icmp_pkt_size, - pkt_interval, target_interval, pid, table, packets, number_of_targets, - program_state); + pkt_interval, target_interval, sender_id, table, packets, + number_of_targets, program_state); } } @@ -915,8 +915,8 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff_to_now(prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { - wait_for_reply(icmp_sock, final_wait, icmp_pkt_size, pkt_interval, target_interval, pid, - table, packets, number_of_targets, program_state); + wait_for_reply(icmp_sock, final_wait, icmp_pkt_size, pkt_interval, target_interval, + sender_id, table, packets, number_of_targets, program_state); } } } @@ -932,8 +932,8 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo * icmp echo reply : the rest */ static int wait_for_reply(int sock, const time_t time_interval, unsigned short icmp_pkt_size, - unsigned int *pkt_interval, unsigned int *target_interval, pid_t pid, - ping_target **table, const unsigned short packets, + unsigned int *pkt_interval, unsigned int *target_interval, + uint16_t sender_id, ping_target **table, const unsigned short packets, const unsigned short number_of_targets, check_icmp_state *program_state) { union icmp_packet packet; if (!(packet.buf = malloc(icmp_pkt_size))) { @@ -1027,16 +1027,16 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i : sizeof(struct icmp));*/ if ((address_family == PF_INET && - (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || + (ntohs(packet.icp->icmp_id) != sender_id || packet.icp->icmp_type != ICMP_ECHOREPLY || ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) || (address_family == PF_INET6 && - (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || + (ntohs(packet.icp6->icmp6_id) != sender_id || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) { if (debug > 2) { printf("not a proper ICMP_ECHOREPLY\n"); } - handle_random_icmp(buf + hlen, &resp_addr, pkt_interval, target_interval, pid, table, + handle_random_icmp(buf + hlen, &resp_addr, pkt_interval, target_interval, sender_id, table, packets, number_of_targets, program_state); continue; @@ -1136,7 +1136,7 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i /* the ping functions */ static int send_icmp_ping(const int sock, ping_target *host, const unsigned short icmp_pkt_size, - const pid_t pid, check_icmp_state *program_state) { + const uint16_t sender_id, check_icmp_state *program_state) { if (sock == -1) { errno = 0; crash("Attempt to send on bogus socket"); @@ -1174,7 +1174,7 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; icp->icmp_cksum = 0; - icp->icmp_id = htons((uint16_t)pid); + icp->icmp_id = htons((uint16_t)sender_id); icp->icmp_seq = htons(host->id++); icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); @@ -1192,7 +1192,7 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor icp6->icmp6_type = ICMP6_ECHO_REQUEST; icp6->icmp6_code = 0; icp6->icmp6_cksum = 0; - icp6->icmp6_id = htons((uint16_t)pid); + icp6->icmp6_id = htons((uint16_t)sender_id); icp6->icmp6_seq = htons(host->id++); // let checksum be calculated automatically @@ -1330,9 +1330,10 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, } static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, - bool jitter_mode, bool score_mode, int min_hosts_alive, threshold warn, - threshold crit, const int icmp_sock, const unsigned short number_of_targets, - check_icmp_state *program_state, ping_target *target_list) { + bool jitter_mode, bool score_mode, int min_hosts_alive, + check_icmp_threshold warn, check_icmp_threshold crit, const int icmp_sock, + const unsigned short number_of_targets, check_icmp_state *program_state, + ping_target *target_list) { // Deactivate alarm alarm(0); @@ -1821,13 +1822,12 @@ static add_target_wrapper add_target(char *arg, const int mode) { address_family = AF_INET; sin = (struct sockaddr_in *)&address_storage; error_code = inet_pton(address_family, arg, &sin->sin_addr); -#ifdef USE_IPV6 + if (error_code != 1) { address_family = AF_INET6; sin6 = (struct sockaddr_in6 *)&address_storage; error_code = inet_pton(address_family, arg, &sin6->sin6_addr); } -#endif /* If we don't find any valid addresses, we still don't know the address_family */ if (error_code != 1) { address_family = -1; @@ -2029,7 +2029,7 @@ static unsigned int get_timevar(const char *str) { } /* not too good at checking errors, but it'll do (main() should barfe on -1) */ -static int get_threshold(char *str, threshold *threshold) { +static int get_threshold(char *str, check_icmp_threshold *threshold) { if (!str || !strlen(str) || !threshold) { return -1; } @@ -2075,8 +2075,8 @@ static int get_threshold(char *str, threshold *threshold) { * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score * (exclusively) */ -static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, - threshold_mode mode) { +static bool get_threshold2(char *str, size_t length, check_icmp_threshold *warn, + check_icmp_threshold *crit, threshold_mode mode) { if (!str || !length || !warn || !crit) { return false; } @@ -2106,8 +2106,8 @@ static bool get_threshold2(char *str, size_t length, threshold *warn, threshold return parse_threshold2_helper(work_pointer, strlen(work_pointer), warn, mode); } -static bool parse_threshold2_helper(char *threshold_string, size_t length, threshold *thr, - threshold_mode mode) { +static bool parse_threshold2_helper(char *threshold_string, size_t length, + check_icmp_threshold *thr, threshold_mode mode) { char *resultChecker = {0}; switch (mode) { diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 2efe6e59..2a521b04 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -10,8 +10,6 @@ unsigned int timeout = DEFAULT_TIMEOUT; check_icmp_config check_icmp_config_init() { check_icmp_config tmp = { - .source_ip = NULL, - .order_mode = false, .mos_mode = false, .rta_mode = false, @@ -20,10 +18,6 @@ check_icmp_config check_icmp_config_init() { .score_mode = false, .min_hosts_alive = -1, - .icmp_data_size = DEFAULT_PING_DATA_SIZE, - .icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN, - .pkt_interval = DEFAULT_PKT_INTERVAL, - .target_interval = 0, .crit = {.pl = DEFAULT_CRIT_PL, .rta = DEFAULT_CRIT_RTA, .jitter = 50.0, @@ -34,11 +28,18 @@ check_icmp_config check_icmp_config_init() { .jitter = 40.0, .mos = 3.5, .score = 80.0}, - .pid = {}, - .mode = MODE_RTA, + .ttl = DEFAULT_TTL, + .icmp_data_size = DEFAULT_PING_DATA_SIZE, + .icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN, + .pkt_interval = DEFAULT_PKT_INTERVAL, + .target_interval = 0, + .number_of_packets = DEFAULT_NUMBER_OF_PACKETS, + .source_ip = NULL, + + .sender_id = {}, - .packets = DEFAULT_NUMBER_OF_PACKETS, + .mode = MODE_RTA, .number_of_targets = 0, .targets = NULL, @@ -168,10 +169,10 @@ unsigned int ping_target_list_append(ping_target *list, ping_target *elem) { return result; } -void check_icmp_timeout_handler(int signal, siginfo_t * info, void *ucontext) { +void check_icmp_timeout_handler(int signal, siginfo_t *info, void *ucontext) { // Ignore unused arguments - (void) info; - (void) ucontext; + (void)info; + (void)ucontext; mp_subcheck timeout_sc = mp_subcheck_init(); timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state); diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index deae9bec..3599c0f0 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -10,20 +10,38 @@ #include #include #include +#include #include "./check_icmp_helpers.h" /* threshold structure. all values are maximum allowed, exclusive */ -typedef struct threshold { +typedef struct { unsigned char pl; /* max allowed packet loss in percent */ unsigned int rta; /* roundtrip time average, microseconds */ double jitter; /* jitter time average, microseconds */ double mos; /* MOS */ double score; /* Score */ -} threshold; +} check_icmp_threshold; -typedef struct { - char *source_ip; +/* the different modes of this program are as follows: + * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping) + * MODE_HOSTCHECK: Return immediately upon any sign of life + * In addition, sends packets to ALL addresses assigned + * to this host (as returned by gethostbyname() or + * gethostbyaddr() and expects one host only to be checked at + * a time. Therefore, any packet response what so ever will + * count as a sign of life, even when received outside + * crit.rta limit. Do not misspell any additional IP's. + * MODE_ALL: Requires packets from ALL requested IP to return OK (default). + * MODE_ICMP: Default Mode + */ +typedef enum { + MODE_RTA, + MODE_HOSTCHECK, + MODE_ALL, + MODE_ICMP, +} check_icmp_execution_mode; +typedef struct { bool order_mode; bool mos_mode; bool rta_mode; @@ -32,18 +50,20 @@ typedef struct { bool score_mode; int min_hosts_alive; + check_icmp_threshold crit; + check_icmp_threshold warn; + + unsigned long ttl; unsigned short icmp_data_size; unsigned short icmp_pkt_size; unsigned int pkt_interval; unsigned int target_interval; - threshold crit; - threshold warn; - pid_t pid; + unsigned short number_of_packets; + char *source_ip; - int mode; - unsigned long ttl; + uint16_t sender_id; // PID of the main process, which is used as an ID in packets - unsigned short packets; + check_icmp_execution_mode mode; unsigned short number_of_targets; ping_target *targets; @@ -78,24 +98,6 @@ typedef struct icmp_ping_data { #define DEFAULT_TIMEOUT 10 #define DEFAULT_TTL 64 -/* the different modes of this program are as follows: - * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping) - * MODE_HOSTCHECK: Return immediately upon any sign of life - * In addition, sends packets to ALL addresses assigned - * to this host (as returned by gethostbyname() or - * gethostbyaddr() and expects one host only to be checked at - * a time. Therefore, any packet response what so ever will - * count as a sign of life, even when received outside - * crit.rta limit. Do not misspell any additional IP's. - * MODE_ALL: Requires packets from ALL requested IP to return OK (default). - * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without - * tcp and udp args does this) - */ -#define MODE_RTA 0 -#define MODE_HOSTCHECK 1 -#define MODE_ALL 2 -#define MODE_ICMP 3 - #define DEFAULT_NUMBER_OF_PACKETS 5 #define PACKET_BACKOFF_FACTOR 1.5 -- cgit v1.2.3-74-g34f1 From 9ebde5eb09dbf4b869ffd6f501d007b9b264e1a9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 6 May 2025 00:01:42 +0200 Subject: WIP - check_icmp refactor 4 --- plugins-root/check_icmp.c | 220 +++++++++++++++++++++++++++++++--------------- 1 file changed, 147 insertions(+), 73 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 9d163678..94f20eec 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -149,20 +149,37 @@ static int wait_for_reply(int socket, time_t time_interval, unsigned short icmp_ static ssize_t recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, struct sockaddr * /*saddr*/, time_t *timeout, struct timeval * /*tv*/); static int handle_random_icmp(unsigned char * /*packet*/, struct sockaddr_storage * /*addr*/, - unsigned int *pkt_interval, unsigned int *target_interval, uint16_t sender_id, - ping_target **table, unsigned short packets, + unsigned int *pkt_interval, unsigned int *target_interval, + uint16_t sender_id, ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); /* Sending data */ -static int send_icmp_ping(int /*sock*/, ping_target * /*host*/, unsigned short icmp_pkt_size, +static int send_icmp_ping(int socket, ping_target *host, unsigned short icmp_pkt_size, uint16_t sender_id, check_icmp_state *program_state); /* Threshold related */ -static int get_threshold(char *str, check_icmp_threshold *threshold); -static bool get_threshold2(char *str, size_t length, check_icmp_threshold * /*warn*/, - check_icmp_threshold * /*crit*/, threshold_mode mode); -static bool parse_threshold2_helper(char *threshold_string, size_t length, - check_icmp_threshold *thr, threshold_mode mode); +typedef struct { + int errorcode; + check_icmp_threshold threshold; +} get_threshold_wrapper; +static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold); + +typedef struct { + int errorcode; + check_icmp_threshold warn; + check_icmp_threshold crit; +} get_threshold2_wrapper; +static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn, + check_icmp_threshold crit, threshold_mode mode); + +typedef struct { + int errorcode; + check_icmp_threshold result; +} parse_threshold2_helper_wrapper; +static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string, + size_t length, + check_icmp_threshold thr, + threshold_mode mode); /* main test function */ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, @@ -314,7 +331,6 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { /* Reset argument scanning */ optind = 1; - bool err; /* parse the arguments */ for (int i = 1; i < argc; i++) { long int arg; @@ -341,12 +357,22 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { case 'I': result.config.target_interval = get_timevar(optarg); break; - case 'w': - get_threshold(optarg, &result.config.warn); - break; - case 'c': - get_threshold(optarg, &result.config.crit); - break; + case 'w': { + get_threshold_wrapper warn = get_threshold(optarg, result.config.warn); + if (warn.errorcode == OK) { + result.config.warn = warn.threshold; + } else { + crash("failed to parse warning threshold"); + } + } break; + case 'c': { + get_threshold_wrapper crit = get_threshold(optarg, result.config.crit); + if (crit.errorcode == OK) { + result.config.crit = crit.threshold; + } else { + crash("failed to parse critical threshold"); + } + } break; case 'n': case 'p': result.config.number_of_packets = (unsigned short)strtoul(optarg, NULL, 0); @@ -387,51 +413,65 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { print_help(); exit(STATE_UNKNOWN); break; - case 'R': /* RTA mode */ - err = get_threshold2(optarg, strlen(optarg), &result.config.warn, - &result.config.crit, const_rta_mode); - if (!err) { + case 'R': /* RTA mode */ { + get_threshold2_wrapper rta_th = get_threshold2( + optarg, strlen(optarg), result.config.warn, result.config.crit, const_rta_mode); + + if (rta_th.errorcode != OK) { crash("Failed to parse RTA threshold"); } + result.config.warn = rta_th.warn; + result.config.crit = rta_th.crit; result.config.rta_mode = true; - break; - case 'P': /* packet loss mode */ - err = get_threshold2(optarg, strlen(optarg), &result.config.warn, - &result.config.crit, const_packet_loss_mode); - if (!err) { + } break; + case 'P': /* packet loss mode */ { + get_threshold2_wrapper pl_th = + get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit, + const_packet_loss_mode); + if (pl_th.errorcode != OK) { crash("Failed to parse packet loss threshold"); } + result.config.warn = pl_th.warn; + result.config.crit = pl_th.crit; result.config.pl_mode = true; - break; - case 'J': /* jitter mode */ - err = get_threshold2(optarg, strlen(optarg), &result.config.warn, - &result.config.crit, const_jitter_mode); - if (!err) { + } break; + case 'J': /* jitter mode */ { + get_threshold2_wrapper jitter_th = + get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit, + const_jitter_mode); + if (jitter_th.errorcode != OK) { crash("Failed to parse jitter threshold"); } + result.config.warn = jitter_th.warn; + result.config.crit = jitter_th.crit; result.config.jitter_mode = true; - break; - case 'M': /* MOS mode */ - err = get_threshold2(optarg, strlen(optarg), &result.config.warn, - &result.config.crit, const_mos_mode); - if (!err) { + } break; + case 'M': /* MOS mode */ { + get_threshold2_wrapper mos_th = get_threshold2( + optarg, strlen(optarg), result.config.warn, result.config.crit, const_mos_mode); + if (mos_th.errorcode != OK) { crash("Failed to parse MOS threshold"); } + result.config.warn = mos_th.warn; + result.config.crit = mos_th.crit; result.config.mos_mode = true; - break; - case 'S': /* score mode */ - err = get_threshold2(optarg, strlen(optarg), &result.config.warn, - &result.config.crit, const_score_mode); - if (!err) { + } break; + case 'S': /* score mode */ { + get_threshold2_wrapper score_th = + get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit, + const_score_mode); + if (score_th.errorcode != OK) { crash("Failed to parse score threshold"); } + result.config.warn = score_th.warn; + result.config.crit = score_th.crit; result.config.score_mode = true; - break; + } break; case 'O': /* out of order mode */ result.config.order_mode = true; break; @@ -644,7 +684,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad char address[INET6_ADDRSTRLEN]; parse_address(addr, address, sizeof(address)); printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", - get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address, host->name); + get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address, + host->name); } program_state->icmp_lost++; @@ -1030,14 +1071,15 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i (ntohs(packet.icp->icmp_id) != sender_id || packet.icp->icmp_type != ICMP_ECHOREPLY || ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) || (address_family == PF_INET6 && - (ntohs(packet.icp6->icmp6_id) != sender_id || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || + (ntohs(packet.icp6->icmp6_id) != sender_id || + packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) { if (debug > 2) { printf("not a proper ICMP_ECHOREPLY\n"); } - handle_random_icmp(buf + hlen, &resp_addr, pkt_interval, target_interval, sender_id, table, - packets, number_of_targets, program_state); + handle_random_icmp(buf + hlen, &resp_addr, pkt_interval, target_interval, sender_id, + table, packets, number_of_targets, program_state); continue; } @@ -2029,9 +2071,15 @@ static unsigned int get_timevar(const char *str) { } /* not too good at checking errors, but it'll do (main() should barfe on -1) */ -static int get_threshold(char *str, check_icmp_threshold *threshold) { - if (!str || !strlen(str) || !threshold) { - return -1; +static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) { + get_threshold_wrapper result = { + .errorcode = OK, + .threshold = threshold, + }; + + if (!str || !strlen(str)) { + result.errorcode = ERROR; + return result; } /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ @@ -2042,26 +2090,27 @@ static int get_threshold(char *str, check_icmp_threshold *threshold) { *tmp = '\0'; } else if (*tmp == ',' && is_at_last_char) { *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */ - threshold->pl = (unsigned char)strtoul(tmp + 1, NULL, 0); + result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0); break; } is_at_last_char = true; tmp--; } - threshold->rta = get_timevar(str); + result.threshold.rta = get_timevar(str); - if (!threshold->rta) { - return -1; + if (!result.threshold.rta) { + result.errorcode = ERROR; + return result; } - if (threshold->rta > MAXTTL * 1000000) { - threshold->rta = MAXTTL * 1000000; + if (result.threshold.rta > MAXTTL * 1000000) { + result.threshold.rta = MAXTTL * 1000000; } - if (threshold->pl > 100) { - threshold->pl = 100; + if (result.threshold.pl > 100) { + result.threshold.pl = 100; } - return 0; + return result; } /* @@ -2075,10 +2124,17 @@ static int get_threshold(char *str, check_icmp_threshold *threshold) { * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score * (exclusively) */ -static bool get_threshold2(char *str, size_t length, check_icmp_threshold *warn, - check_icmp_threshold *crit, threshold_mode mode) { - if (!str || !length || !warn || !crit) { - return false; +static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn, + check_icmp_threshold crit, threshold_mode mode) { + get_threshold2_wrapper result = { + .errorcode = OK, + .warn = warn, + .crit = crit, + }; + + if (!str || !length) { + result.errorcode = ERROR; + return result; } // p points to the last char in str @@ -2095,50 +2151,68 @@ static bool get_threshold2(char *str, size_t length, check_icmp_threshold *warn, char *start_of_value = work_pointer + 1; - if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) { - return false; + parse_threshold2_helper_wrapper tmp = + parse_threshold2_helper(start_of_value, strlen(start_of_value), result.crit, mode); + if (tmp.errorcode != OK) { + result.errorcode = ERROR; + return result; } + result.crit = tmp.result; } first_iteration = false; work_pointer--; } - return parse_threshold2_helper(work_pointer, strlen(work_pointer), warn, mode); + parse_threshold2_helper_wrapper tmp = + parse_threshold2_helper(work_pointer, strlen(work_pointer), result.warn, mode); + if (tmp.errorcode != OK) { + result.errorcode = ERROR; + } else { + result.warn = tmp.result; + } + return result; } -static bool parse_threshold2_helper(char *threshold_string, size_t length, - check_icmp_threshold *thr, threshold_mode mode) { +static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string, + size_t length, + check_icmp_threshold thr, + threshold_mode mode) { char *resultChecker = {0}; + parse_threshold2_helper_wrapper result = { + .result = thr, + .errorcode = OK, + }; switch (mode) { case const_rta_mode: - thr->rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000); + result.result.rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000); break; case const_packet_loss_mode: - thr->pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0); + result.result.pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0); break; case const_jitter_mode: - thr->jitter = strtod(threshold_string, &resultChecker); + result.result.jitter = strtod(threshold_string, &resultChecker); break; case const_mos_mode: - thr->mos = strtod(threshold_string, &resultChecker); + result.result.mos = strtod(threshold_string, &resultChecker); break; case const_score_mode: - thr->score = strtod(threshold_string, &resultChecker); + result.result.score = strtod(threshold_string, &resultChecker); break; } if (resultChecker == threshold_string) { // Failed to parse - return false; + result.errorcode = ERROR; + return result; } if (resultChecker != (threshold_string + length)) { // Trailing symbols - return false; + result.errorcode = ERROR; } - return true; + return result; } unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) { -- cgit v1.2.3-74-g34f1 From 4acba2b3ecef7c1482b3cd25e35773947d80e2c6 Mon Sep 17 00:00:00 2001 From: William Date: Thu, 27 Mar 2025 11:20:36 +1000 Subject: Improve handling of -4/-6 If fping is used with a target that has dual stack v4/v6, then due to the logic during command construction, ipv4 will never be checked as v6 is preferred by fping. This explicitly flags -4/-6 when it is requested by the user. --- plugins/check_fping.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/plugins/check_fping.c b/plugins/check_fping.c index ec7abb67..1c2b7689 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -79,7 +79,29 @@ int main(int argc, char **argv) { server = strscpy(server, config.server_name); char *option_string = ""; + char *fping_prog = NULL; + /* compose the command */ +#ifdef PATH_TO_FPING6 + if (address_family != AF_INET && is_inet6_addr(server)) { + fping_prog = strdup(PATH_TO_FPING6); + } else { + xasprintf(&option_string, "%s-4 ", option_string); + fping_prog = strdup(PATH_TO_FPING); + } +#else + if (address_family != AF_INET) { + // -4 / -6 must be set explicitly as when a host has dual stack + // if we don't specify -4 then fping selects ipv6 which can mess + // with some checks. + xasprintf(&option_string, "%s-6 ", option_string); + } else { + xasprintf(&option_string, "%s-4 ", option_string); + } + + fping_prog = strdup(PATH_TO_FPING); +#endif + if (config.target_timeout) { xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout); } @@ -99,17 +121,6 @@ int main(int argc, char **argv) { xasprintf(&option_string, "%s-R ", option_string); } - char *fping_prog = NULL; -#ifdef PATH_TO_FPING6 - if (address_family != AF_INET && is_inet6_addr(server)) { - fping_prog = strdup(PATH_TO_FPING6); - } else { - fping_prog = strdup(PATH_TO_FPING); - } -#else - fping_prog = strdup(PATH_TO_FPING); -#endif - char *command_line = NULL; xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server); -- cgit v1.2.3-74-g34f1 From a1472be88322140cf1f2fc39b9e1b654a86f8155 Mon Sep 17 00:00:00 2001 From: William Date: Fri, 28 Mar 2025 12:40:35 +1000 Subject: Harden check with unspec --- plugins/check_fping.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_fping.c b/plugins/check_fping.c index 1c2b7689..cf9c2d1c 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -83,14 +83,14 @@ int main(int argc, char **argv) { /* compose the command */ #ifdef PATH_TO_FPING6 - if (address_family != AF_INET && is_inet6_addr(server)) { + if (address_family == AF_INET6 || (address_family == AF_UNSPEC && is_inet6_addr(server))) { fping_prog = strdup(PATH_TO_FPING6); } else { xasprintf(&option_string, "%s-4 ", option_string); fping_prog = strdup(PATH_TO_FPING); } #else - if (address_family != AF_INET) { + if (address_family == AF_INET6 || (address_family == AF_UNSPEC && is_inet6_addr(server))) { // -4 / -6 must be set explicitly as when a host has dual stack // if we don't specify -4 then fping selects ipv6 which can mess // with some checks. -- cgit v1.2.3-74-g34f1 From 58a34245110f12192d3b3e99198086c119a57418 Mon Sep 17 00:00:00 2001 From: William Date: Wed, 2 Apr 2025 10:50:56 +1000 Subject: Improve logic --- plugins/check_fping.c | 48 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/plugins/check_fping.c b/plugins/check_fping.c index cf9c2d1c..9dadcec7 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -81,27 +81,61 @@ int main(int argc, char **argv) { char *option_string = ""; char *fping_prog = NULL; - /* compose the command */ + /* First determine if the target is dualstack or ipv6 only. */ +#ifdef USE_IPV6 + bool server_is_inet6_addr = is_inet6_addr(server); +#else + bool server_is_inet6_addr = false; +#endif + + /* PATH_TO_FPING6 implies USE_IPV6 */ #ifdef PATH_TO_FPING6 - if (address_family == AF_INET6 || (address_family == AF_UNSPEC && is_inet6_addr(server))) { + /* + * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack + * -> we use ipv6 + * If the user requested -4 OR the user made no assertion and the address is v4 ONLY + * -> we use ipv4 + */ + if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) { fping_prog = strdup(PATH_TO_FPING6); } else { xasprintf(&option_string, "%s-4 ", option_string); fping_prog = strdup(PATH_TO_FPING); } #else - if (address_family == AF_INET6 || (address_family == AF_UNSPEC && is_inet6_addr(server))) { - // -4 / -6 must be set explicitly as when a host has dual stack - // if we don't specify -4 then fping selects ipv6 which can mess - // with some checks. +#ifdef USE_IPV6 + /* + * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack + * -> we use ipv6 + * If the user requested -4 OR the user made no assertion and the address is v4 ONLY + * -> we use ipv4 + */ + if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) { xasprintf(&option_string, "%s-6 ", option_string); } else { xasprintf(&option_string, "%s-4 ", option_string); } - +#else + /* + * If the user requested -6 + * -> warn that v6 is not available + * -> we use ipv4 + */ + if (address_family == AF_INET6) { + usage4(_("IPv6 support not available")); + } + /* + * Note here we still have to call with -4, else in the case of a dual stacked target + * we could potentially silently use ipv6 despite having just warned that it is not available + */ + xasprintf(&option_string, "%s-4 ", option_string); + /* end USE_IPV6 */ +#endif fping_prog = strdup(PATH_TO_FPING); + /* end PATH_TO_FPING6 */ #endif + /* compose the command */ if (config.target_timeout) { xasprintf(&option_string, "%s-t %d ", option_string, config.target_timeout); } -- cgit v1.2.3-74-g34f1 From 1fb9300a2f10d5c649df484e1b8d7550f9a5f846 Mon Sep 17 00:00:00 2001 From: William Date: Wed, 7 May 2025 13:17:47 +1000 Subject: Remove un-needed flags --- configure.ac | 7 ------- plugins/check_fping.c | 42 ------------------------------------------ 2 files changed, 49 deletions(-) diff --git a/configure.ac b/configure.ac index fdc9b699..bd3de196 100644 --- a/configure.ac +++ b/configure.ac @@ -1524,17 +1524,10 @@ AC_PATH_PROG(PATH_TO_FPING6,fping6) AC_ARG_WITH(fping_command, ACX_HELP_STRING([--with-fping-command=PATH], [Path to fping command]), PATH_TO_FPING=$withval) -AC_ARG_WITH(fping6_command, - ACX_HELP_STRING([--with-fping6-command=PATH], - [Path to fping6 command]), PATH_TO_FPING6=$withval) - if test -n "$PATH_TO_FPING" then AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping]) EXTRAS="$EXTRAS check_fping\$(EXEEXT)" - if test x"$with_ipv6" != xno && test -n "$PATH_TO_FPING6"; then - AC_DEFINE_UNQUOTED(PATH_TO_FPING6,"$PATH_TO_FPING6",[path to fping6]) - fi else AC_MSG_WARN([Get fping from http://www.fping.com in order to make check_fping plugin]) fi diff --git a/plugins/check_fping.c b/plugins/check_fping.c index 9dadcec7..e05056b2 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -82,28 +82,8 @@ int main(int argc, char **argv) { char *fping_prog = NULL; /* First determine if the target is dualstack or ipv6 only. */ -#ifdef USE_IPV6 bool server_is_inet6_addr = is_inet6_addr(server); -#else - bool server_is_inet6_addr = false; -#endif - /* PATH_TO_FPING6 implies USE_IPV6 */ -#ifdef PATH_TO_FPING6 - /* - * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack - * -> we use ipv6 - * If the user requested -4 OR the user made no assertion and the address is v4 ONLY - * -> we use ipv4 - */ - if (address_family == AF_INET6 || (address_family == AF_UNSPEC && server_is_inet6_addr)) { - fping_prog = strdup(PATH_TO_FPING6); - } else { - xasprintf(&option_string, "%s-4 ", option_string); - fping_prog = strdup(PATH_TO_FPING); - } -#else -#ifdef USE_IPV6 /* * If the user requested -6 OR the user made no assertion and the address is v6 or dualstack * -> we use ipv6 @@ -115,25 +95,7 @@ int main(int argc, char **argv) { } else { xasprintf(&option_string, "%s-4 ", option_string); } -#else - /* - * If the user requested -6 - * -> warn that v6 is not available - * -> we use ipv4 - */ - if (address_family == AF_INET6) { - usage4(_("IPv6 support not available")); - } - /* - * Note here we still have to call with -4, else in the case of a dual stacked target - * we could potentially silently use ipv6 despite having just warned that it is not available - */ - xasprintf(&option_string, "%s-4 ", option_string); - /* end USE_IPV6 */ -#endif fping_prog = strdup(PATH_TO_FPING); - /* end PATH_TO_FPING6 */ -#endif /* compose the command */ if (config.target_timeout) { @@ -385,11 +347,7 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) { address_family = AF_INET; break; case '6': /* IPv6 only */ -#ifdef USE_IPV6 address_family = AF_INET6; -#else - usage(_("IPv6 support not available\n")); -#endif break; case 'c': get_threshold(optarg, rv); -- cgit v1.2.3-74-g34f1 From 236188e92fbc81e0d05f79ee1688daedbaf204dc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Thu, 8 May 2025 08:59:36 +0200 Subject: Do not explicitely disable IPv6 in the CI --- .github/workflows/test-next.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml index 81240759..fd59e85d 100644 --- a/.github/workflows/test-next.yml +++ b/.github/workflows/test-next.yml @@ -38,7 +38,7 @@ jobs: ${{ matrix.distro }} \ /bin/sh -c '${{ matrix.prepare }} && \ tools/setup && \ - ./configure --enable-libtap --with-ipv6=no && \ + ./configure --enable-libtap && \ make && \ make test && \ make dist && \ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 77ca6585..ce0ec547 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: ${{ matrix.distro }} \ /bin/sh -c '${{ matrix.prepare }} && \ tools/setup && \ - ./configure --enable-libtap --with-ipv6=no && \ + ./configure --enable-libtap && \ make && \ make test && \ make dist && \ -- cgit v1.2.3-74-g34f1 From ec47bbbda6b99f0efc1801a424812a4dd457f9b6 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Fri, 9 May 2025 10:49:02 +0200 Subject: changed filename in cmd_file_read to const char * (check_apt warning) --- lib/utils_cmd.c | 2 +- lib/utils_cmd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index 18350ac0..9b222409 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c @@ -346,7 +346,7 @@ int cmd_run_array(char *const *argv, output *out, output *err, int flags) { return _cmd_close(fd); } -int cmd_file_read(char *filename, output *out, int flags) { +int cmd_file_read(const char *filename, output *out, int flags) { int fd; if (out) memset(out, 0, sizeof(output)); diff --git a/lib/utils_cmd.h b/lib/utils_cmd.h index d00069c9..728ece23 100644 --- a/lib/utils_cmd.h +++ b/lib/utils_cmd.h @@ -20,7 +20,7 @@ typedef struct output output; /** prototypes **/ int cmd_run(const char *, output *, output *, int); int cmd_run_array(char *const *, output *, output *, int); -int cmd_file_read(char *, output *, int); +int cmd_file_read(const char *, output *, int); /* only multi-threaded plugins need to bother with this */ void cmd_init(void); -- cgit v1.2.3-74-g34f1 From 6b86583e0d3db92cc5e87af97f4d204bc0e797f3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 16 May 2025 09:07:35 +0200 Subject: WIP - check_icmp refactor 5 --- plugins-root/check_icmp.c | 27 +++++++++++++++----------- plugins-root/check_icmp.d/check_icmp_helpers.c | 4 ++-- plugins-root/check_icmp.d/check_icmp_helpers.h | 4 ++-- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 94f20eec..8565f32d 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -133,12 +133,12 @@ void print_help(); void print_usage(void); /* Time related */ -static unsigned int get_timevar(const char * /*str*/); +static unsigned int get_timevar(const char *str); static time_t get_timevaldiff(struct timeval earlier, struct timeval later); static time_t get_timevaldiff_to_now(struct timeval earlier); -static in_addr_t get_ip_address(const char * /*ifname*/); -static void set_source_ip(char * /*arg*/, int icmp_sock); +static in_addr_t get_ip_address(const char *ifname); +static void set_source_ip(char *arg, int icmp_sock); /* Receiving data */ static int wait_for_reply(int socket, time_t time_interval, unsigned short icmp_pkt_size, @@ -146,9 +146,9 @@ static int wait_for_reply(int socket, time_t time_interval, unsigned short icmp_ uint16_t sender_id, ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); -static ssize_t recvfrom_wto(int /*sock*/, void * /*buf*/, unsigned int /*len*/, - struct sockaddr * /*saddr*/, time_t *timeout, struct timeval * /*tv*/); -static int handle_random_icmp(unsigned char * /*packet*/, struct sockaddr_storage * /*addr*/, +static ssize_t recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, + time_t *timeout, struct timeval *received_timestamp); +static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, unsigned int *pkt_interval, unsigned int *target_interval, uint16_t sender_id, ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); @@ -193,20 +193,25 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo check_icmp_state *program_state, ping_target *target_list); /* Target aquisition */ +typedef struct { + int error_code; + check_icmp_target_container host; +} add_host_wrapper; +static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode); typedef struct { int error_code; ping_target *targets; unsigned int number_of_targets; } add_target_wrapper; -static add_target_wrapper add_target(char * /*arg*/, int mode); +static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode); typedef struct { int error_code; ping_target *target; } add_target_ip_wrapper; -static add_target_ip_wrapper add_target_ip(char * /*arg*/, struct sockaddr_storage * /*in*/); +static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *address); -static void parse_address(struct sockaddr_storage * /*addr*/, char * /*address*/, socklen_t size); +static void parse_address(struct sockaddr_storage *addr, char *address, socklen_t size); static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size); @@ -218,7 +223,7 @@ static void finish(int /*sig*/, bool order_mode, bool mos_mode, bool rta_mode, b ping_target *target_list); /* Error exit */ -static void crash(const char * /*fmt*/, ...); +static void crash(const char *fmt, ...); /** global variables **/ static int debug = 0; @@ -1852,7 +1857,7 @@ static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *a } /* wrapper for add_target_ip */ -static add_target_wrapper add_target(char *arg, const int mode) { +static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode) { struct sockaddr_storage address_storage; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 2a521b04..58e13581 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -73,7 +73,7 @@ check_icmp_state check_icmp_state_init() { return tmp; } -rta_host_create_wrapper rta_host_create(char *name, struct sockaddr_storage *address) { +ping_target_create_wrapper ping_target_create(char *name, struct sockaddr_storage *address) { struct sockaddr_in *sin; struct sockaddr_in6 *sin6; if (address_family == AF_INET) { @@ -82,7 +82,7 @@ rta_host_create_wrapper rta_host_create(char *name, struct sockaddr_storage *add sin6 = (struct sockaddr_in6 *)address; } - rta_host_create_wrapper result = { + ping_target_create_wrapper result = { .errorcode = OK, }; diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h index 7e8a4d9f..3e798f72 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.h +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -68,9 +68,9 @@ check_icmp_state check_icmp_state_init(); typedef struct { int errorcode; ping_target host; -} rta_host_create_wrapper; +} ping_target_create_wrapper; -rta_host_create_wrapper rta_host_create(char *name, struct sockaddr_storage *address); +ping_target_create_wrapper ping_target_create(char *name, struct sockaddr_storage *address); unsigned int ping_target_list_append(ping_target *list, ping_target *elem); void check_icmp_timeout_handler(int, siginfo_t *, void *); -- cgit v1.2.3-74-g34f1 From 322cd6f829e55a91edbfc42201775afb552b5660 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 17 May 2025 11:26:27 +0200 Subject: Lib: Add perfdata for char and unsigned char --- lib/perfdata.c | 8 ++++++++ lib/perfdata.h | 4 ++++ plugins-root/Makefile.am | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/perfdata.c b/lib/perfdata.c index f425ffcf..b87de7e0 100644 --- a/lib/perfdata.c +++ b/lib/perfdata.c @@ -257,6 +257,10 @@ mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { return pd; } +mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { return mp_set_pd_value_long_long(pd, (long long)value); } + +mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } + mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); } mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } @@ -288,6 +292,10 @@ mp_perfdata_value mp_create_pd_value_double(double value) { mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); } +mp_perfdata_value mp_create_pd_value_char(char value) { return mp_create_pd_value_long_long((long long)value); } + +mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } + mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); } mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } diff --git a/lib/perfdata.h b/lib/perfdata.h index cb552678..7fd908a9 100644 --- a/lib/perfdata.h +++ b/lib/perfdata.h @@ -155,6 +155,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long); _Generic((V), \ float: mp_create_pd_value_float, \ double: mp_create_pd_value_double, \ + char: mp_create_pd_value_char, \ + unsigned char: mp_create_pd_value_u_char, \ int: mp_create_pd_value_int, \ unsigned int: mp_create_pd_value_u_int, \ long: mp_create_pd_value_long, \ @@ -164,6 +166,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long); mp_perfdata_value mp_create_pd_value_float(float); mp_perfdata_value mp_create_pd_value_double(double); +mp_perfdata_value mp_create_pd_value_char(char); +mp_perfdata_value mp_create_pd_value_u_char(unsigned char); mp_perfdata_value mp_create_pd_value_int(int); mp_perfdata_value mp_create_pd_value_u_int(unsigned int); mp_perfdata_value mp_create_pd_value_long(long); diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am index f09f5e07..fffc0575 100644 --- a/plugins-root/Makefile.am +++ b/plugins-root/Makefile.am @@ -91,7 +91,7 @@ pst3_LDFLAGS = @PST3CFLAGS@ # pst3 must not use monitoringplug/gnulib includes! pst3_CPPFLAGS = -check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) +check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) clean-local: -- cgit v1.2.3-74-g34f1 From a01d522c4cac11eb31510758d42fe744eb21471d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 17 May 2025 11:26:54 +0200 Subject: WIP - check_icmp refactor 6 --- plugins-root/check_icmp.c | 704 ++++++++++++------------- plugins-root/check_icmp.d/check_icmp_helpers.c | 22 +- plugins-root/check_icmp.d/check_icmp_helpers.h | 13 +- plugins-root/check_icmp.d/config.h | 4 + 4 files changed, 360 insertions(+), 383 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 8565f32d..34adf6fe 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -46,6 +46,8 @@ const char *email = "devel@monitoring-plugins.org"; #include "../plugins/common.h" #include "netutils.h" #include "utils.h" +#include "output.h" +#include "perfdata.h" #if HAVE_SYS_SOCKIO_H # include @@ -182,8 +184,7 @@ static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_s threshold_mode mode); /* main test function */ -static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, - bool jitter_mode, bool score_mode, int min_hosts_alive, +static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, unsigned short icmp_pkt_size, unsigned int *pkt_interval, unsigned int *target_interval, check_icmp_threshold warn, check_icmp_threshold crit, uint16_t sender_id, @@ -191,6 +192,8 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo struct timeval prog_start, ping_target **table, unsigned short packets, int icmp_sock, unsigned short number_of_targets, check_icmp_state *program_state, ping_target *target_list); +mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, + check_icmp_threshold warn, check_icmp_threshold crit); /* Target aquisition */ typedef struct { @@ -198,6 +201,7 @@ typedef struct { check_icmp_target_container host; } add_host_wrapper; static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode); + typedef struct { int error_code; ping_target *targets; @@ -216,11 +220,10 @@ static void parse_address(struct sockaddr_storage *addr, char *address, socklen_ static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size); /* End of run function */ -static void finish(int /*sig*/, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, - bool jitter_mode, bool score_mode, int min_hosts_alive, +static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive, check_icmp_threshold warn, check_icmp_threshold crit, int icmp_sock, unsigned short number_of_targets, check_icmp_state *program_state, - ping_target *target_list); + ping_target *target_list) __attribute__((noreturn)); /* Error exit */ static void crash(const char *fmt, ...); @@ -336,6 +339,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { /* Reset argument scanning */ optind = 1; + int host_counter = 0; /* parse the arguments */ for (int i = 1; i < argc; i++) { long int arg; @@ -391,15 +395,20 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { // TODO die here and complain about wrong input break; case 'H': { - add_target_wrapper add_result = add_target(optarg, result.config.mode); - if (add_result.error_code == OK) { + add_host_wrapper host_add_result = add_host(optarg, result.config.mode); + if (host_add_result.error_code == OK) { + result.config.hosts[host_counter] = host_add_result.host; + host_counter++; + if (result.config.targets != NULL) { - result.config.number_of_targets += - ping_target_list_append(result.config.targets, add_result.targets); + result.config.number_of_targets += ping_target_list_append( + result.config.targets, host_add_result.host.target_list); } else { - result.config.targets = add_result.targets; - result.config.number_of_targets += add_result.number_of_targets; + result.config.targets = host_add_result.host.target_list; + result.config.number_of_targets += host_add_result.host.number_of_targets; } + } else { + // TODO adding host failed, crash here } } break; case 'l': @@ -428,7 +437,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.warn = rta_th.warn; result.config.crit = rta_th.crit; - result.config.rta_mode = true; + result.config.modes.rta_mode = true; } break; case 'P': /* packet loss mode */ { get_threshold2_wrapper pl_th = @@ -440,7 +449,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.warn = pl_th.warn; result.config.crit = pl_th.crit; - result.config.pl_mode = true; + result.config.modes.pl_mode = true; } break; case 'J': /* jitter mode */ { get_threshold2_wrapper jitter_th = @@ -452,7 +461,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.warn = jitter_th.warn; result.config.crit = jitter_th.crit; - result.config.jitter_mode = true; + result.config.modes.jitter_mode = true; } break; case 'M': /* MOS mode */ { get_threshold2_wrapper mos_th = get_threshold2( @@ -463,7 +472,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.warn = mos_th.warn; result.config.crit = mos_th.crit; - result.config.mos_mode = true; + result.config.modes.mos_mode = true; } break; case 'S': /* score mode */ { get_threshold2_wrapper score_th = @@ -475,10 +484,10 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.warn = score_th.warn; result.config.crit = score_th.crit; - result.config.score_mode = true; + result.config.modes.score_mode = true; } break; case 'O': /* out of order mode */ - result.config.order_mode = true; + result.config.modes.order_mode = true; break; } } @@ -869,22 +878,19 @@ int main(int argc, char **argv) { check_icmp_state program_state = check_icmp_state_init(); - run_checks(config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, - config.jitter_mode, config.score_mode, config.min_hosts_alive, config.icmp_data_size, + run_checks(config.modes, config.min_hosts_alive, config.icmp_data_size, &pkt_interval, &target_interval, config.warn, config.crit, config.sender_id, config.mode, max_completion_time, prog_start, table, config.number_of_packets, icmp_sock, config.number_of_targets, &program_state, config.targets); errno = 0; - finish(0, config.order_mode, config.mos_mode, config.rta_mode, config.pl_mode, - config.jitter_mode, config.score_mode, config.min_hosts_alive, config.warn, config.crit, + finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit, icmp_sock, config.number_of_targets, &program_state, config.targets); return (0); } -static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, - bool jitter_mode, bool score_mode, int min_hosts_alive, +static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, unsigned short icmp_pkt_size, unsigned int *pkt_interval, unsigned int *target_interval, check_icmp_threshold warn, check_icmp_threshold crit, const uint16_t sender_id, @@ -900,7 +906,7 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) { /* don't send useless packets */ if (!targets_alive(number_of_targets, program_state->targets_down)) { - finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, + finish(0, modes, min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state, target_list); } @@ -947,7 +953,7 @@ static void run_checks(bool order_mode, bool mos_mode, bool rta_mode, bool pl_mo if (debug) { printf("Time passed. Finishing up\n"); } - finish(0, order_mode, mos_mode, rta_mode, pl_mode, jitter_mode, score_mode, + finish(0, modes, min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state, target_list); } @@ -1139,7 +1145,7 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i /* Check if packets in order */ if (target->last_icmp_seq >= packet.icp->icmp_seq) { - target->order_status = STATE_CRITICAL; + target->found_out_of_order_packets = true; } } target->last_tdiff = tdiff; @@ -1376,8 +1382,7 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, return (ret); } -static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool pl_mode, - bool jitter_mode, bool score_mode, int min_hosts_alive, +static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, check_icmp_threshold warn, check_icmp_threshold crit, const int icmp_sock, const unsigned short number_of_targets, check_icmp_state *program_state, ping_target *target_list) { @@ -1399,358 +1404,53 @@ static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool targets_alive(number_of_targets, program_state->targets_down)); } - /* iterate thrice to calculate values, give output, and print perfparse */ - mp_state_enum status = STATE_OK; - ping_target *host = target_list; + mp_check overall = mp_check_init(); - unsigned int target_counter = 0; - const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"}; + // loop over targets to evaluate each one + ping_target *host = target_list; int hosts_ok = 0; int hosts_warn = 0; while (host) { - mp_state_enum this_status = STATE_OK; - - unsigned char packet_loss; - double rta; - if (!host->icmp_recv) { - /* rta 0 is ofcourse not entirely correct, but will still show up - * conspicuously as missing entries in perfparse and cacti */ - packet_loss = 100; - rta = 0; - status = STATE_CRITICAL; - /* up the down counter if not already counted */ - if (!(host->flags & FLAG_LOST_CAUSE) && - targets_alive(number_of_targets, program_state->targets_down)) { - program_state->targets_down++; - } - } else { - packet_loss = - (unsigned char)((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent; - rta = (double)host->time_waited / host->icmp_recv; - } - - if (host->icmp_recv > 1) { - /* - * This algorithm is probably pretty much blindly copied from - * locations like this one: - * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS - * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick - * research MOS originates from the Audio/Video transport network area. Whether it can - * and should be computed from ICMP data, I can not say. - * - * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value - * - * MOS stands likely for Mean Opinion Score ( - * https://en.wikipedia.org/wiki/Mean_Opinion_Score ) - * - * More links: - * - https://confluence.slac.stanford.edu/display/IEPM/MOS - */ - host->jitter = (host->jitter / (host->icmp_recv - 1) / 1000); - - /* - * Take the average round trip latency (in milliseconds), add - * round trip jitter, but double the impact to latency - * then add 10 for protocol latencies (in milliseconds). - */ - host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10; - - double R; - if (host->EffectiveLatency < 160) { - R = 93.2 - (host->EffectiveLatency / 40); - } else { - R = 93.2 - ((host->EffectiveLatency - 120) / 10); - } - - // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a - // loss of 5% will be entered as 5). - R = R - (packet_loss * 2.5); - - if (R < 0) { - R = 0; - } - - host->score = R; - host->mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R)); - } else { - host->jitter = 0; - host->jitter_min = 0; - host->jitter_max = 0; - host->mos = 0; - } - - host->pl = packet_loss; - host->rta = rta; - - /* if no new mode selected, use old schema */ - if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) { - rta_mode = true; - pl_mode = true; - } - - /* Check which mode is on and do the warn / Crit stuff */ - if (rta_mode) { - if (rta >= crit.rta) { - this_status = STATE_CRITICAL; - status = STATE_CRITICAL; - host->rta_status = STATE_CRITICAL; - } else if (status != STATE_CRITICAL && (rta >= warn.rta)) { - this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status); - status = STATE_WARNING; - host->rta_status = STATE_WARNING; - } - } - - if (pl_mode) { - if (packet_loss >= crit.pl) { - this_status = STATE_CRITICAL; - status = STATE_CRITICAL; - host->pl_status = STATE_CRITICAL; - } else if (status != STATE_CRITICAL && (packet_loss >= warn.pl)) { - this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status); - status = STATE_WARNING; - host->pl_status = STATE_WARNING; - } + if (host->flags & FLAG_LOST_CAUSE) { + program_state->targets_down++; } + // TODO call evaluate here + mp_subcheck sc_target = evaluate_target(*host, modes, warn, crit); - if (jitter_mode) { - if (host->jitter >= crit.jitter) { - this_status = STATE_CRITICAL; - status = STATE_CRITICAL; - host->jitter_status = STATE_CRITICAL; - } else if (status != STATE_CRITICAL && (host->jitter >= warn.jitter)) { - this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status); - status = STATE_WARNING; - host->jitter_status = STATE_WARNING; - } - } + mp_add_subcheck_to_check(&overall, sc_target); - if (mos_mode) { - if (host->mos <= crit.mos) { - this_status = STATE_CRITICAL; - status = STATE_CRITICAL; - host->mos_status = STATE_CRITICAL; - } else if (status != STATE_CRITICAL && (host->mos <= warn.mos)) { - this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status); - status = STATE_WARNING; - host->mos_status = STATE_WARNING; - } - } - - if (score_mode) { - if (host->score <= crit.score) { - this_status = STATE_CRITICAL; - status = STATE_CRITICAL; - host->score_status = STATE_CRITICAL; - } else if (status != STATE_CRITICAL && (host->score <= warn.score)) { - this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status); - status = STATE_WARNING; - host->score_status = STATE_WARNING; - } - } - - if (this_status == STATE_WARNING) { - hosts_warn++; - } else if (this_status == STATE_OK) { + mp_state_enum target_state = mp_compute_subcheck_state(sc_target); + if (target_state == STATE_OK) { hosts_ok++; + } else if (target_state == STATE_WARNING) { + hosts_warn++; } host = host->next; } /* this is inevitable */ - if (!targets_alive(number_of_targets, program_state->targets_down)) { - status = STATE_CRITICAL; - } - if (min_hosts_alive > -1) { - if (hosts_ok >= min_hosts_alive) { - status = STATE_OK; - } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { - status = STATE_WARNING; - } - } - printf("%s - ", status_string[status]); - - host = target_list; - while (host) { - if (debug) { - puts(""); - } - - if (target_counter) { - if (target_counter < number_of_targets) { - printf(" :: "); - } else { - printf("\n"); - } - } - - target_counter++; - - if (!host->icmp_recv) { - status = STATE_CRITICAL; - host->rtmin = 0; - host->jitter_min = 0; - - if (host->flags & FLAG_LOST_CAUSE) { - char address[INET6_ADDRSTRLEN]; - parse_address(&host->error_addr, address, sizeof(address)); - printf("%s: %s @ %s. rta nan, lost %d%%", host->name, - get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100); - } else { /* not marked as lost cause, so we have no flags for it */ - printf("%s: rta nan, lost 100%%", host->name); - } - } else { /* !icmp_recv */ - printf("%s", host->name); - /* rta text output */ - if (rta_mode) { - if (status == STATE_OK) { - printf(" rta %0.3fms", host->rta / 1000); - } else if (status == STATE_WARNING && host->rta_status == status) { - printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, - (float)warn.rta / 1000); - } else if (status == STATE_CRITICAL && host->rta_status == status) { - printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, - (float)crit.rta / 1000); - } - } - - /* pl text output */ - if (pl_mode) { - if (status == STATE_OK) { - printf(" lost %u%%", host->pl); - } else if (status == STATE_WARNING && host->pl_status == status) { - printf(" lost %u%% > %u%%", host->pl, warn.pl); - } else if (status == STATE_CRITICAL && host->pl_status == status) { - printf(" lost %u%% > %u%%", host->pl, crit.pl); - } - } - - /* jitter text output */ - if (jitter_mode) { - if (status == STATE_OK) { - printf(" jitter %0.3fms", (float)host->jitter); - } else if (status == STATE_WARNING && host->jitter_status == status) { - printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter); - } else if (status == STATE_CRITICAL && host->jitter_status == status) { - printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter); - } - } - - /* mos text output */ - if (mos_mode) { - if (status == STATE_OK) { - printf(" MOS %0.1f", (float)host->mos); - } else if (status == STATE_WARNING && host->mos_status == status) { - printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos); - } else if (status == STATE_CRITICAL && host->mos_status == status) { - printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos); - } - } - - /* score text output */ - if (score_mode) { - if (status == STATE_OK) { - printf(" Score %u", (int)host->score); - } else if (status == STATE_WARNING && host->score_status == status) { - printf(" Score %u < %u", (int)host->score, (int)warn.score); - } else if (status == STATE_CRITICAL && host->score_status == status) { - printf(" Score %u < %u", (int)host->score, (int)crit.score); - } - } - - /* order statis text output */ - if (order_mode) { - if (status == STATE_OK) { - printf(" Packets in order"); - } else if (status == STATE_CRITICAL && host->order_status == status) { - printf(" Packets out of order"); - } - } - } - host = host->next; - } - - /* iterate once more for pretty perfparse output */ - if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) { - printf("|"); - } - - target_counter = 0; - host = target_list; - while (host) { - if (debug) { - puts(""); - } - - if (rta_mode) { - if (host->pl < 100) { - printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", - (number_of_targets > 1) ? host->name : "", host->rta / 1000, - (float)warn.rta / 1000, (float)crit.rta / 1000, - (number_of_targets > 1) ? host->name : "", (float)host->rtmax / 1000, - (number_of_targets > 1) ? host->name : "", - (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0); - } else { - printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", - (number_of_targets > 1) ? host->name : "", - (number_of_targets > 1) ? host->name : "", - (number_of_targets > 1) ? host->name : ""); - } - } - - if (pl_mode) { - printf("%spl=%u%%;%u;%u;0;100 ", (number_of_targets > 1) ? host->name : "", host->pl, - warn.pl, crit.pl); - } - - if (jitter_mode) { - if (host->pl < 100) { - printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; " - "%sjitter_min=%0.3fms;;;; ", - (number_of_targets > 1) ? host->name : "", (float)host->jitter, - (float)warn.jitter, (float)crit.jitter, - (number_of_targets > 1) ? host->name : "", (float)host->jitter_max / 1000, - (number_of_targets > 1) ? host->name : "", (float)host->jitter_min / 1000); - } else { - printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", - (number_of_targets > 1) ? host->name : "", - (number_of_targets > 1) ? host->name : "", - (number_of_targets > 1) ? host->name : ""); - } - } - - if (mos_mode) { - if (host->pl < 100) { - printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (number_of_targets > 1) ? host->name : "", - (float)host->mos, (float)warn.mos, (float)crit.mos); - } else { - printf("%smos=U;;;; ", (number_of_targets > 1) ? host->name : ""); - } - } - - if (score_mode) { - if (host->pl < 100) { - printf("%sscore=%u;%u;%u;0;100 ", (number_of_targets > 1) ? host->name : "", - (int)host->score, (int)warn.score, (int)crit.score); - } else { - printf("%sscore=U;;;; ", (number_of_targets > 1) ? host->name : ""); - } - } - - host = host->next; + if (targets_alive(number_of_targets, program_state->targets_down) == 0) { + mp_subcheck sc_no_target_alive = mp_subcheck_init(); + sc_no_target_alive = mp_set_subcheck_state(sc_no_target_alive, STATE_CRITICAL); + sc_no_target_alive.output = strdup("No target is alive!"); + mp_add_subcheck_to_check(&overall, sc_no_target_alive); } if (min_hosts_alive > -1) { + mp_subcheck sc_min_targets_alive = mp_subcheck_init(); + sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK); + + // TODO problably broken now if (hosts_ok >= min_hosts_alive) { - status = STATE_OK; + // TODO this should overwrite the main state + sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK); } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { - status = STATE_WARNING; + sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING); } } /* finish with an empty line */ - puts(""); if (debug) { printf( "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", @@ -1758,7 +1458,7 @@ static void finish(int sig, bool order_mode, bool mos_mode, bool rta_mode, bool hosts_ok, hosts_warn, min_hosts_alive); } - exit(status); + mp_exit(overall); } static time_t get_timevaldiff(const struct timeval earlier, const struct timeval later) { @@ -1825,7 +1525,7 @@ static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *a // } /* add the fresh ip */ - ping_target *target = (ping_target *)malloc(sizeof(ping_target)); + ping_target *target = (ping_target *)calloc(1, sizeof(ping_target)); if (!target) { char straddr[INET6_ADDRSTRLEN]; parse_address((struct sockaddr_storage *)&address, straddr, sizeof(straddr)); @@ -2339,3 +2039,289 @@ void print_usage(void) { printf("%s\n", _("Usage:")); printf(" %s [options] [-H] host1 host2 hostN\n", progname); } + +static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode) { + add_host_wrapper result = { + .error_code = OK, + .host = check_icmp_target_container_init(), + }; + + add_target_wrapper targets = add_target(arg, mode); + + if (targets.error_code != OK) { + result.error_code = targets.error_code; + return result; + } + + result.host = check_icmp_target_container_init(); + + result.host.name = strdup(arg); + result.host.target_list = targets.targets; + result.host.number_of_targets = targets.number_of_targets; + + return result; +} + +mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, + check_icmp_threshold warn, check_icmp_threshold crit) { + /* if no new mode selected, use old schema */ + if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode && !modes.mos_mode && !modes.order_mode) { + modes.rta_mode = true; + modes.pl_mode = true; + } + + mp_subcheck result = mp_subcheck_init(); + result = mp_set_subcheck_default_state(result, STATE_OK); + xasprintf(&result.output, "%s", target.name); + + double packet_loss; + double rta; + if (!target.icmp_recv) { + /* rta 0 is of course not entirely correct, but will still show up + * conspicuously as missing entries in perfparse and cacti */ + packet_loss = 100; + rta = 0; + result = mp_set_subcheck_state(result, STATE_CRITICAL); + /* up the down counter if not already counted */ + + if (target.flags & FLAG_LOST_CAUSE) { + char address[INET6_ADDRSTRLEN]; + parse_address(&target.error_addr, address, sizeof(address)); + xasprintf(&result.output, "%s: %s @ %s", result.output, + get_icmp_error_msg(target.icmp_type, target.icmp_code), address); + } else { /* not marked as lost cause, so we have no flags for it */ + xasprintf(&result.output, "%s", result.output); + } + } else { + packet_loss = + (unsigned char)((target.icmp_sent - target.icmp_recv) * 100) / target.icmp_sent; + rta = (double)target.time_waited / target.icmp_recv; + } + + double EffectiveLatency; + double mos; /* Mean opinion score */ + double score; /* score */ + + if (target.icmp_recv > 1) { + /* + * This algorithm is probably pretty much blindly copied from + * locations like this one: + * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS + * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick + * research MOS originates from the Audio/Video transport network area. Whether it can + * and should be computed from ICMP data, I can not say. + * + * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value + * + * MOS stands likely for Mean Opinion Score ( + * https://en.wikipedia.org/wiki/Mean_Opinion_Score ) + * + * More links: + * - https://confluence.slac.stanford.edu/display/IEPM/MOS + */ + target.jitter = (target.jitter / (target.icmp_recv - 1) / 1000); + + /* + * Take the average round trip latency (in milliseconds), add + * round trip jitter, but double the impact to latency + * then add 10 for protocol latencies (in milliseconds). + */ + EffectiveLatency = (rta / 1000) + target.jitter * 2 + 10; + + double R; + if (EffectiveLatency < 160) { + R = 93.2 - (EffectiveLatency / 40); + } else { + R = 93.2 - ((EffectiveLatency - 120) / 10); + } + + // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a + // loss of 5% will be entered as 5). + R = R - (packet_loss * 2.5); + + if (R < 0) { + R = 0; + } + + score = R; + mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R)); + } else { + target.jitter = 0; + target.jitter_min = 0; + target.jitter_max = 0; + mos = 0; + } + + /* Check which mode is on and do the warn / Crit stuff */ + if (modes.rta_mode) { + mp_subcheck sc_rta = mp_subcheck_init(); + sc_rta = mp_set_subcheck_default_state(sc_rta, STATE_OK); + xasprintf(&sc_rta.output, "rta %0.3fms", rta / 1000); + + if (rta >= crit.rta) { + sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL); + xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, crit.rta / 1000); + } else if (rta >= warn.rta) { + sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING); + xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, warn.rta / 1000); + } + + if (packet_loss < 100) { + mp_perfdata pd_rta = perfdata_init(); + xasprintf(&pd_rta.label, "%srta", target.name); + pd_rta.uom = strdup("ms"); + pd_rta.value = mp_create_pd_value(rta / 1000); + pd_rta.min = mp_create_pd_value(0); + + pd_rta.warn = mp_range_set_end(pd_rta.warn, mp_create_pd_value(warn.rta)); + pd_rta.crit = mp_range_set_end(pd_rta.crit, mp_create_pd_value(crit.rta)); + mp_add_perfdata_to_subcheck(&sc_rta, pd_rta); + + mp_perfdata pd_rt_min = perfdata_init(); + xasprintf(&pd_rt_min.label, "%srtmin", target.name); + pd_rt_min.value = mp_create_pd_value(target.rtmin / 1000); + pd_rt_min.uom = strdup("ms"); + mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_min); + + mp_perfdata pd_rt_max = perfdata_init(); + xasprintf(&pd_rt_max.label, "%srtmax", target.name); + pd_rt_max.value = mp_create_pd_value(target.rtmax / 1000); + pd_rt_max.uom = strdup("ms"); + mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_max); + } + + mp_add_subcheck_to_subcheck(&result, sc_rta); + } + + if (modes.pl_mode) { + mp_subcheck sc_pl = mp_subcheck_init(); + sc_pl = mp_set_subcheck_default_state(sc_pl, STATE_OK); + xasprintf(&sc_pl.output, "packet loss %.1f%%", packet_loss); + + if (packet_loss >= crit.pl) { + sc_pl = mp_set_subcheck_state(sc_pl, STATE_CRITICAL); + xasprintf(&sc_pl.output, "%s > %u%%", sc_pl.output, crit.pl); + } else if (packet_loss >= warn.pl) { + sc_pl = mp_set_subcheck_state(sc_pl, STATE_WARNING); + xasprintf(&sc_pl.output, "%s > %u%%", sc_pl.output, crit.pl); + } + + mp_perfdata pd_pl = perfdata_init(); + xasprintf(&pd_pl.label, "%spl", target.name); + pd_pl.uom = strdup("%"); + + pd_pl.warn = mp_range_set_end(pd_pl.warn, mp_create_pd_value(warn.pl)); + pd_pl.crit = mp_range_set_end(pd_pl.crit, mp_create_pd_value(crit.pl)); + pd_pl.value = mp_create_pd_value(packet_loss); + + mp_add_perfdata_to_subcheck(&sc_pl, pd_pl); + + mp_add_subcheck_to_subcheck(&result, sc_pl); + } + + if (modes.jitter_mode) { + mp_subcheck sc_jitter = mp_subcheck_init(); + sc_jitter = mp_set_subcheck_default_state(sc_jitter, STATE_OK); + xasprintf(&sc_jitter.output, "jitter %0.3fms", target.jitter); + + if (target.jitter >= crit.jitter) { + sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_CRITICAL); + xasprintf(&sc_jitter.output, "%s > %0.3fms", sc_jitter.output, crit.jitter); + } else if (target.jitter >= warn.jitter) { + sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_WARNING); + xasprintf(&sc_jitter.output, "%s > %0.3fms", sc_jitter.output, warn.jitter); + } + + if (packet_loss < 100) { + mp_perfdata pd_jitter = perfdata_init(); + pd_jitter.uom = strdup("ms"); + xasprintf(&pd_jitter.label, "%sjitter_avg", target.name); + pd_jitter.value = mp_create_pd_value(target.jitter); + pd_jitter.warn = mp_range_set_end(pd_jitter.warn, mp_create_pd_value(warn.jitter)); + pd_jitter.crit = mp_range_set_end(pd_jitter.crit, mp_create_pd_value(crit.jitter)); + mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter); + + mp_perfdata pd_jitter_min = perfdata_init(); + pd_jitter_min.uom = strdup("ms"); + xasprintf(&pd_jitter_min.label, "%sjitter_min", target.name); + pd_jitter_min.value = mp_create_pd_value(target.jitter_min); + mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_min); + + mp_perfdata pd_jitter_max = perfdata_init(); + pd_jitter_max.uom = strdup("ms"); + xasprintf(&pd_jitter_max.label, "%sjitter_max", target.name); + pd_jitter_max.value = mp_create_pd_value(target.jitter_max); + mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_max); + } + mp_add_subcheck_to_subcheck(&result, sc_jitter); + } + + if (modes.mos_mode) { + mp_subcheck sc_mos = mp_subcheck_init(); + sc_mos = mp_set_subcheck_default_state(sc_mos, STATE_OK); + xasprintf(&sc_mos.output, "MOS %0.1f", mos); + + if (mos <= crit.mos) { + sc_mos = mp_set_subcheck_state(sc_mos, STATE_CRITICAL); + xasprintf(&sc_mos.output, "%s < %0.1f", sc_mos.output, crit.mos); + } else if (mos <= warn.mos) { + sc_mos = mp_set_subcheck_state(sc_mos, STATE_WARNING); + xasprintf(&sc_mos.output, "%s < %0.1f", sc_mos.output, warn.mos); + } + + if (packet_loss < 100) { + mp_perfdata pd_mos = perfdata_init(); + xasprintf(&pd_mos.label, "%smos", target.name); + pd_mos.value = mp_create_pd_value(mos); + pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos)); + pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos)); + pd_mos.min = mp_create_pd_value(0); + pd_mos.max = mp_create_pd_value(5); + mp_add_perfdata_to_subcheck(&sc_mos, pd_mos); + } + mp_add_subcheck_to_subcheck(&result, sc_mos); + } + + if (modes.score_mode) { + mp_subcheck sc_score = mp_subcheck_init(); + sc_score = mp_set_subcheck_default_state(sc_score, STATE_OK); + xasprintf(&sc_score.output, "Score %u", score); + + if (score <= crit.score) { + sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL); + xasprintf(&sc_score.output, "%s < %u", sc_score.output, crit.score); + } else if (score <= warn.score) { + sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING); + xasprintf(&sc_score.output, "%s < %u", sc_score.output, warn.score); + } + + if (packet_loss < 100) { + mp_perfdata pd_score = perfdata_init(); + xasprintf(&pd_score.label, "%sscore", target.name); + pd_score.value = mp_create_pd_value(score); + pd_score.warn = mp_range_set_end(pd_score.warn, mp_create_pd_value(warn.score)); + pd_score.crit = mp_range_set_end(pd_score.crit, mp_create_pd_value(crit.score)); + pd_score.min = mp_create_pd_value(0); + pd_score.max = mp_create_pd_value(100); + mp_add_perfdata_to_subcheck(&sc_score, pd_score); + } + + mp_add_subcheck_to_subcheck(&result, sc_score); + } + + if (modes.order_mode) { + mp_subcheck sc_order = mp_subcheck_init(); + sc_order = mp_set_subcheck_default_state(sc_order, STATE_OK); + + if (target.found_out_of_order_packets) { + mp_set_subcheck_state(sc_order, STATE_CRITICAL); + xasprintf(&sc_order.output, "Packets out of order"); + } else { + xasprintf(&sc_order.output, "Packets in order"); + } + + mp_add_subcheck_to_subcheck(&result, sc_order); + } + + return result; +} diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 58e13581..a2b54c6a 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -10,12 +10,15 @@ unsigned int timeout = DEFAULT_TIMEOUT; check_icmp_config check_icmp_config_init() { check_icmp_config tmp = { - .order_mode = false, - .mos_mode = false, - .rta_mode = false, - .pl_mode = false, - .jitter_mode = false, - .score_mode = false, + .modes = + { + .order_mode = false, + .mos_mode = false, + .rta_mode = false, + .pl_mode = false, + .jitter_mode = false, + .score_mode = false, + }, .min_hosts_alive = -1, .crit = {.pl = DEFAULT_CRIT_PL, @@ -56,12 +59,7 @@ ping_target ping_target_init() { .jitter_min = INFINITY, - .rta_status = STATE_OK, - .jitter_status = STATE_OK, - .mos_status = STATE_OK, - .score_status = STATE_OK, - .pl_status = STATE_OK, - .order_status = STATE_OK, + .found_out_of_order_packets = false, }; return tmp; diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h index 3e798f72..68c39b4a 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.h +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -20,7 +20,6 @@ typedef struct rta_host { unsigned char icmp_type, icmp_code; /* type and code from errors */ unsigned short flags; /* control/status flags */ - double rta; /* measured RTA */ double rtmax; /* max rtt */ double rtmin; /* min rtt */ @@ -28,20 +27,10 @@ typedef struct rta_host { double jitter_max; /* jitter rtt maximum */ double jitter_min; /* jitter rtt minimum */ - double EffectiveLatency; - double mos; /* Mean opinion score */ - double score; /* score */ - time_t last_tdiff; unsigned int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */ - unsigned char pl; /* measured packet loss */ - mp_state_enum rta_status; // check result for RTA checks - mp_state_enum jitter_status; // check result for Jitter checks - mp_state_enum mos_status; // check result for MOS checks - mp_state_enum score_status; // check result for score checks - mp_state_enum pl_status; // check result for packet loss checks - mp_state_enum order_status; // check result for packet order checks + bool found_out_of_order_packets; struct rta_host *next; } ping_target; diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index 3599c0f0..aa33c991 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -48,6 +48,10 @@ typedef struct { bool pl_mode; bool jitter_mode; bool score_mode; +} check_icmp_mode_switches; + +typedef struct { + check_icmp_mode_switches modes; int min_hosts_alive; check_icmp_threshold crit; -- cgit v1.2.3-74-g34f1 From fbcd11acb7ae098db28a778c84756a49b36f83ef Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 00:20:36 +0200 Subject: Clang-format --- plugins-root/check_icmp.c | 25 ++++++++++++------------- plugins-root/check_icmp.d/check_icmp_helpers.h | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 34adf6fe..58f5ae70 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -878,14 +878,14 @@ int main(int argc, char **argv) { check_icmp_state program_state = check_icmp_state_init(); - run_checks(config.modes, config.min_hosts_alive, config.icmp_data_size, - &pkt_interval, &target_interval, config.warn, config.crit, config.sender_id, - config.mode, max_completion_time, prog_start, table, config.number_of_packets, - icmp_sock, config.number_of_targets, &program_state, config.targets); + run_checks(config.modes, config.min_hosts_alive, config.icmp_data_size, &pkt_interval, + &target_interval, config.warn, config.crit, config.sender_id, config.mode, + max_completion_time, prog_start, table, config.number_of_packets, icmp_sock, + config.number_of_targets, &program_state, config.targets); errno = 0; - finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit, - icmp_sock, config.number_of_targets, &program_state, config.targets); + finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit, icmp_sock, + config.number_of_targets, &program_state, config.targets); return (0); } @@ -906,9 +906,8 @@ static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) { /* don't send useless packets */ if (!targets_alive(number_of_targets, program_state->targets_down)) { - finish(0, modes, - min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state, - target_list); + finish(0, modes, min_hosts_alive, warn, crit, icmp_sock, number_of_targets, + program_state, target_list); } if (table[target_index]->flags & FLAG_LOST_CAUSE) { if (debug) { @@ -953,9 +952,8 @@ static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, if (debug) { printf("Time passed. Finishing up\n"); } - finish(0, modes, - min_hosts_alive, warn, crit, icmp_sock, number_of_targets, program_state, - target_list); + finish(0, modes, min_hosts_alive, warn, crit, icmp_sock, number_of_targets, + program_state, target_list); } /* catch the packets that might come in within the timeframe, but @@ -2065,7 +2063,8 @@ static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode) { mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, check_icmp_threshold warn, check_icmp_threshold crit) { /* if no new mode selected, use old schema */ - if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode && !modes.mos_mode && !modes.order_mode) { + if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode && + !modes.mos_mode && !modes.order_mode) { modes.rta_mode = true; modes.pl_mode = true; } diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h index 68c39b4a..5f771635 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.h +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -15,7 +15,7 @@ typedef struct rta_host { char *msg; /* icmp error message, if any */ struct sockaddr_storage saddr_in; /* the address of this host */ struct sockaddr_storage error_addr; /* stores address of error replies */ - time_t time_waited; /* total time waited, in usecs */ + time_t time_waited; /* total time waited, in usecs */ unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ unsigned char icmp_type, icmp_code; /* type and code from errors */ unsigned short flags; /* control/status flags */ -- cgit v1.2.3-74-g34f1 From 8f08e7ab3ecc03ea38062fe4442668fbea30bb73 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 14:00:55 +0200 Subject: WIP - check_icmp refactor 7 --- plugins-root/check_icmp.c | 193 ++++++++++++++++++++++++++-------------------- 1 file changed, 108 insertions(+), 85 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 58f5ae70..0614d6aa 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -184,17 +184,24 @@ static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_s threshold_mode mode); /* main test function */ -static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, - unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, check_icmp_threshold warn, - check_icmp_threshold crit, uint16_t sender_id, +static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, + unsigned int *target_interval, uint16_t sender_id, check_icmp_execution_mode mode, unsigned int max_completion_time, struct timeval prog_start, ping_target **table, unsigned short packets, int icmp_sock, unsigned short number_of_targets, - check_icmp_state *program_state, ping_target *target_list); + check_icmp_state *program_state); mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, check_icmp_threshold warn, check_icmp_threshold crit); +typedef struct { + int targets_ok; + int targets_warn; + mp_subcheck sc_host; +} evaluate_host_wrapper; +evaluate_host_wrapper evaluate_host(check_icmp_target_container host, + check_icmp_mode_switches modes, check_icmp_threshold warn, + check_icmp_threshold crit); + /* Target aquisition */ typedef struct { int error_code; @@ -213,7 +220,7 @@ typedef struct { int error_code; ping_target *target; } add_target_ip_wrapper; -static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *address); +static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address); static void parse_address(struct sockaddr_storage *addr, char *address, socklen_t size); @@ -223,7 +230,8 @@ static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size); static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive, check_icmp_threshold warn, check_icmp_threshold crit, int icmp_sock, unsigned short number_of_targets, check_icmp_state *program_state, - ping_target *target_list) __attribute__((noreturn)); + check_icmp_target_container host_list[], unsigned short number_of_hosts, + mp_check overall[static 1]); /* Error exit */ static void crash(const char *fmt, ...); @@ -247,8 +255,6 @@ typedef struct { int errorcode; check_icmp_config config; } check_icmp_config_wrapper; -check_icmp_config_wrapper process_arguments(int argc, char **argv); - check_icmp_config_wrapper process_arguments(int argc, char **argv) { /* get calling name the old-fashioned way for portability instead * of relying on the glibc-ism __progname */ @@ -313,7 +319,11 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { break; case 'H': { result.config.number_of_hosts++; + break; } + case 'v': + debug++; + break; } } } @@ -345,9 +355,6 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { long int arg; while ((arg = getopt(argc, argv, opts_str)) != EOF) { switch (arg) { - case 'v': - debug++; - break; case 'b': { long size = strtol(optarg, NULL, 0); if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && @@ -859,6 +866,7 @@ int main(int argc, char **argv) { crash("minimum alive hosts is negative (%i)", config.min_hosts_alive); } + // Build an index table of all targets ping_target *host = config.targets; ping_target **table = malloc(sizeof(ping_target *) * config.number_of_targets); if (!table) { @@ -878,27 +886,26 @@ int main(int argc, char **argv) { check_icmp_state program_state = check_icmp_state_init(); - run_checks(config.modes, config.min_hosts_alive, config.icmp_data_size, &pkt_interval, - &target_interval, config.warn, config.crit, config.sender_id, config.mode, - max_completion_time, prog_start, table, config.number_of_packets, icmp_sock, - config.number_of_targets, &program_state, config.targets); + run_checks(config.icmp_data_size, &pkt_interval, &target_interval, config.sender_id, + config.mode, max_completion_time, prog_start, table, config.number_of_packets, + icmp_sock, config.number_of_targets, &program_state); errno = 0; + + mp_check overall = mp_check_init(); finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit, icmp_sock, - config.number_of_targets, &program_state, config.targets); + config.number_of_targets, &program_state, config.hosts, config.number_of_hosts, + &overall); - return (0); + mp_exit(overall); } -static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, - unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, check_icmp_threshold warn, - check_icmp_threshold crit, const uint16_t sender_id, +static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, + unsigned int *target_interval, const uint16_t sender_id, const check_icmp_execution_mode mode, const unsigned int max_completion_time, const struct timeval prog_start, ping_target **table, const unsigned short packets, const int icmp_sock, - const unsigned short number_of_targets, check_icmp_state *program_state, - ping_target *target_list) { + const unsigned short number_of_targets, check_icmp_state *program_state) { /* this loop might actually violate the pkt_interval or target_interval * settings, but only if there aren't any packets on the wire which * indicates that the target can handle an increased packet rate */ @@ -906,8 +913,7 @@ static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) { /* don't send useless packets */ if (!targets_alive(number_of_targets, program_state->targets_down)) { - finish(0, modes, min_hosts_alive, warn, crit, icmp_sock, number_of_targets, - program_state, target_list); + return; } if (table[target_index]->flags & FLAG_LOST_CAUSE) { if (debug) { @@ -952,8 +958,7 @@ static void run_checks(check_icmp_mode_switches modes, int min_hosts_alive, if (debug) { printf("Time passed. Finishing up\n"); } - finish(0, modes, min_hosts_alive, warn, crit, icmp_sock, number_of_targets, - program_state, target_list); + return; } /* catch the packets that might come in within the timeframe, but @@ -1383,7 +1388,8 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, check_icmp_threshold warn, check_icmp_threshold crit, const int icmp_sock, const unsigned short number_of_targets, check_icmp_state *program_state, - ping_target *target_list) { + check_icmp_target_container host_list[], unsigned short number_of_hosts, + mp_check overall[static 1]) { // Deactivate alarm alarm(0); @@ -1402,29 +1408,21 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, targets_alive(number_of_targets, program_state->targets_down)); } - mp_check overall = mp_check_init(); - // loop over targets to evaluate each one - ping_target *host = target_list; - int hosts_ok = 0; - int hosts_warn = 0; - while (host) { - if (host->flags & FLAG_LOST_CAUSE) { - program_state->targets_down++; - } - // TODO call evaluate here - mp_subcheck sc_target = evaluate_target(*host, modes, warn, crit); + int targets_ok = 0; + int targets_warn = 0; + for (unsigned short i = 0; i < number_of_hosts; i++) { + evaluate_host_wrapper host_check = evaluate_host(host_list[i], modes, warn, crit); - mp_add_subcheck_to_check(&overall, sc_target); + targets_ok += host_check.targets_ok; + targets_warn += host_check.targets_warn; - mp_state_enum target_state = mp_compute_subcheck_state(sc_target); - if (target_state == STATE_OK) { - hosts_ok++; - } else if (target_state == STATE_WARNING) { - hosts_warn++; - } + mp_add_subcheck_to_check(overall, host_check.sc_host); + } - host = host->next; + if (number_of_hosts == 1) { + // Exit early here, since the other checks only make sense for multiple hosts + return; } /* this is inevitable */ @@ -1432,7 +1430,7 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, mp_subcheck sc_no_target_alive = mp_subcheck_init(); sc_no_target_alive = mp_set_subcheck_state(sc_no_target_alive, STATE_CRITICAL); sc_no_target_alive.output = strdup("No target is alive!"); - mp_add_subcheck_to_check(&overall, sc_no_target_alive); + mp_add_subcheck_to_check(overall, sc_no_target_alive); } if (min_hosts_alive > -1) { @@ -1440,10 +1438,10 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK); // TODO problably broken now - if (hosts_ok >= min_hosts_alive) { + if (targets_ok >= min_hosts_alive) { // TODO this should overwrite the main state sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK); - } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { + } else if ((targets_ok + targets_warn) >= min_hosts_alive) { sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING); } } @@ -1453,10 +1451,8 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, printf( "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", number_of_targets, targets_alive(number_of_targets, program_state->targets_down), - hosts_ok, hosts_warn, min_hosts_alive); + targets_ok, targets_warn, min_hosts_alive); } - - mp_exit(overall); } static time_t get_timevaldiff(const struct timeval earlier, const struct timeval later) { @@ -1479,13 +1475,18 @@ static time_t get_timevaldiff_to_now(struct timeval earlier) { return get_timevaldiff(earlier, now); } -static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *address) { +static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) { + if (debug) { + char straddr[INET6_ADDRSTRLEN]; + parse_address((struct sockaddr_storage *)&address, straddr, sizeof(straddr)); + printf("add_target_ip called with: %s\n", straddr); + } struct sockaddr_in *sin; struct sockaddr_in6 *sin6; if (address_family == AF_INET) { - sin = (struct sockaddr_in *)address; + sin = (struct sockaddr_in *)&address; } else { - sin6 = (struct sockaddr_in6 *)address; + sin6 = (struct sockaddr_in6 *)&address; } add_target_ip_wrapper result = { @@ -1502,38 +1503,20 @@ static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *a return result; } - // TODO: allow duplicate targets for now, might be on purpose - /* no point in adding two identical IP's, so don't. ;) */ - // struct sockaddr_in *host_sin; - // struct sockaddr_in6 *host_sin6; - // ping_target *host = host_list; - // while (host) { - // host_sin = (struct sockaddr_in *)&host->saddr_in; - // host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; - - // if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) || - // (address_family == AF_INET6 && - // host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { - // if (debug) { - // printf("Identical IP already exists. Not adding %s\n", arg); - // } - // return -1; - // } - // host = host->next; - // } + // get string representation of address + char straddr[INET6_ADDRSTRLEN]; + parse_address((&address), straddr, sizeof(straddr)); /* add the fresh ip */ ping_target *target = (ping_target *)calloc(1, sizeof(ping_target)); if (!target) { - char straddr[INET6_ADDRSTRLEN]; - parse_address((struct sockaddr_storage *)&address, straddr, sizeof(straddr)); - crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(ping_target)); + crash("add_target_ip(%s): malloc(%lu) failed", straddr, sizeof(ping_target)); } *target = ping_target_init(); /* set the values. use calling name for output */ - target->name = strdup(arg); + target->name = strdup(straddr); /* fill out the sockaddr_storage struct */ struct sockaddr_in *host_sin; @@ -1556,9 +1539,13 @@ static add_target_ip_wrapper add_target_ip(char *arg, struct sockaddr_storage *a /* wrapper for add_target_ip */ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode) { - struct sockaddr_storage address_storage; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; + if (debug > 0) { + printf("add_target called with argument %s\n", arg); + } + + struct sockaddr_storage address_storage = {}; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; int error_code = -1; switch (address_family) { @@ -1598,7 +1585,7 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode /* don't resolve if we don't have to */ if (error_code == 1) { /* don't add all ip's if we were given a specific one */ - add_target_ip_wrapper targeted = add_target_ip(arg, &address_storage); + add_target_ip_wrapper targeted = add_target_ip(address_storage); if (targeted.error_code != OK) { result.error_code = ERROR; @@ -1634,7 +1621,8 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode for (struct addrinfo *address = res; address != NULL; address = address->ai_next) { struct sockaddr_storage temporary_ip_address; memcpy(&temporary_ip_address, address->ai_addr, address->ai_addrlen); - add_target_ip_wrapper tmp = add_target_ip(arg, &temporary_ip_address); + + add_target_ip_wrapper tmp = add_target_ip(temporary_ip_address); if (tmp.error_code != OK) { // No proper error handling @@ -2039,6 +2027,10 @@ void print_usage(void) { } static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode) { + if (debug) { + printf("add_host called with argument %s\n", arg); + } + add_host_wrapper result = { .error_code = OK, .host = check_icmp_target_container_init(), @@ -2324,3 +2316,34 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, return result; } + +evaluate_host_wrapper evaluate_host(check_icmp_target_container host, + check_icmp_mode_switches modes, check_icmp_threshold warn, + check_icmp_threshold crit) { + evaluate_host_wrapper result = { + .targets_warn = 0, + .targets_ok = 0, + .sc_host = mp_subcheck_init(), + }; + result.sc_host = mp_set_subcheck_default_state(result.sc_host, STATE_OK); + + result.sc_host.output = strdup(host.name); + + ping_target *target = host.target_list; + for (unsigned int i = 0; i < host.number_of_targets; i++) { + mp_subcheck sc_target = evaluate_target(*target, modes, warn, crit); + + mp_state_enum target_state = mp_compute_subcheck_state(sc_target); + + if (target_state == STATE_WARNING) { + result.targets_warn++; + } else if (target_state == STATE_OK) { + result.targets_ok++; + } + mp_add_subcheck_to_subcheck(&result.sc_host, sc_target); + + target = target->next; + } + + return result; +} -- cgit v1.2.3-74-g34f1 From b5de682309589ddb230e04beaaa54d74f2fe70d5 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 18:10:25 +0200 Subject: WIP - check_icmp refactor 8 --- plugins-root/check_icmp.c | 505 +++++++++++++++---------- plugins-root/check_icmp.d/check_icmp_helpers.c | 64 +--- plugins-root/check_icmp.d/check_icmp_helpers.h | 15 +- plugins-root/check_icmp.d/config.h | 3 + 4 files changed, 332 insertions(+), 255 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 0614d6aa..d4e55b0d 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -71,6 +71,9 @@ const char *email = "devel@monitoring-plugins.org"; #include #include #include +#include +#include +#include #include "../lib/states.h" #include "./check_icmp.d/config.h" @@ -140,24 +143,31 @@ static time_t get_timevaldiff(struct timeval earlier, struct timeval later); static time_t get_timevaldiff_to_now(struct timeval earlier); static in_addr_t get_ip_address(const char *ifname); -static void set_source_ip(char *arg, int icmp_sock); +static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family); /* Receiving data */ -static int wait_for_reply(int socket, time_t time_interval, unsigned short icmp_pkt_size, - unsigned int *pkt_interval, unsigned int *target_interval, - uint16_t sender_id, ping_target **table, unsigned short packets, - unsigned short number_of_targets, check_icmp_state *program_state); +static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval, + unsigned short icmp_pkt_size, unsigned int *pkt_interval, + unsigned int *target_interval, uint16_t sender_id, ping_target **table, + unsigned short packets, unsigned short number_of_targets, + check_icmp_state *program_state); -static ssize_t recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, - time_t *timeout, struct timeval *received_timestamp); +typedef struct { + sa_family_t recv_proto; + ssize_t received; +} recvfrom_wto_wrapper; +static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *buf, unsigned int len, + struct sockaddr *saddr, time_t *timeout, + struct timeval *received_timestamp); static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, unsigned int *pkt_interval, unsigned int *target_interval, uint16_t sender_id, ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); /* Sending data */ -static int send_icmp_ping(int socket, ping_target *host, unsigned short icmp_pkt_size, - uint16_t sender_id, check_icmp_state *program_state); +static int send_icmp_ping(check_icmp_socket_set socket, ping_target *host, + unsigned short icmp_pkt_size, uint16_t sender_id, + check_icmp_state *program_state); /* Threshold related */ typedef struct { @@ -188,7 +198,7 @@ static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, unsigned int *target_interval, uint16_t sender_id, check_icmp_execution_mode mode, unsigned int max_completion_time, struct timeval prog_start, ping_target **table, unsigned short packets, - int icmp_sock, unsigned short number_of_targets, + check_icmp_socket_set sockset, unsigned short number_of_targets, check_icmp_state *program_state); mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, check_icmp_threshold warn, check_icmp_threshold crit); @@ -206,15 +216,21 @@ evaluate_host_wrapper evaluate_host(check_icmp_target_container host, typedef struct { int error_code; check_icmp_target_container host; + bool has_v4; + bool has_v6; } add_host_wrapper; -static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode); +static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode, + sa_family_t enforced_proto); typedef struct { int error_code; ping_target *targets; unsigned int number_of_targets; + bool has_v4; + bool has_v6; } add_target_wrapper; -static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode); +static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode, + sa_family_t enforced_proto); typedef struct { int error_code; @@ -222,13 +238,13 @@ typedef struct { } add_target_ip_wrapper; static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address); -static void parse_address(struct sockaddr_storage *addr, char *address, socklen_t size); +static void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size); static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size); /* End of run function */ static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive, - check_icmp_threshold warn, check_icmp_threshold crit, int icmp_sock, + check_icmp_threshold warn, check_icmp_threshold crit, unsigned short number_of_targets, check_icmp_state *program_state, check_icmp_target_container host_list[], unsigned short number_of_hosts, mp_check overall[static 1]); @@ -298,6 +314,8 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { } } + sa_family_t enforced_ai_family = AF_UNSPEC; + // Parse protocol arguments first // and count hosts here char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64"; @@ -306,16 +324,16 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { while ((arg = getopt(argc, argv, opts_str)) != EOF) { switch (arg) { case '4': - if (address_family != -1) { + if (enforced_ai_family != AF_UNSPEC) { crash("Multiple protocol versions not supported"); } - address_family = AF_INET; + enforced_ai_family = AF_INET; break; case '6': - if (address_family != -1) { + if (enforced_ai_family != AF_UNSPEC) { crash("Multiple protocol versions not supported"); } - address_family = AF_INET6; + enforced_ai_family = AF_INET6; break; case 'H': { result.config.number_of_hosts++; @@ -402,7 +420,8 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { // TODO die here and complain about wrong input break; case 'H': { - add_host_wrapper host_add_result = add_host(optarg, result.config.mode); + add_host_wrapper host_add_result = + add_host(optarg, result.config.mode, enforced_ai_family); if (host_add_result.error_code == OK) { result.config.hosts[host_counter] = host_add_result.host; host_counter++; @@ -414,6 +433,13 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.targets = host_add_result.host.target_list; result.config.number_of_targets += host_add_result.host.number_of_targets; } + + if (host_add_result.has_v4) { + result.config.need_v4 = true; + } + if (host_add_result.has_v6) { + result.config.need_v6 = true; + } } else { // TODO adding host failed, crash here } @@ -502,7 +528,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { argv = &argv[optind]; while (*argv) { - add_target(*argv, result.config.mode); + add_target(*argv, result.config.mode, enforced_ai_family); argv++; } @@ -704,9 +730,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad if (debug) { char address[INET6_ADDRSTRLEN]; parse_address(addr, address, sizeof(address)); - printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", - get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address, - host->name); + printf("Received \"%s\" from %s for ICMP ECHO sent.\n", + get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address); } program_state->icmp_lost++; @@ -732,14 +757,16 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad return 0; } -void parse_address(struct sockaddr_storage *addr, char *address, socklen_t size) { - switch (address_family) { +void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size) { + switch (addr->ss_family) { case AF_INET: - inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); + inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, dst, size); break; case AF_INET6: - inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size); + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, dst, size); break; + default: + assert(false); } } @@ -748,9 +775,6 @@ int main(int argc, char **argv) { bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - address_family = -1; - int icmp_proto = IPPROTO_ICMP; - /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ environ = NULL; @@ -765,35 +789,68 @@ int main(int argc, char **argv) { const check_icmp_config config = tmp_config.config; + // int icmp_proto = IPPROTO_ICMP; // add_target might change address_family - switch (address_family) { - case AF_INET: - icmp_proto = IPPROTO_ICMP; - break; - case AF_INET6: - icmp_proto = IPPROTO_ICMPV6; - break; - default: - crash("Address family not supported"); - } + // switch (address_family) { + // case AF_INET: + // icmp_proto = IPPROTO_ICMP; + // break; + // case AF_INET6: + // icmp_proto = IPPROTO_ICMPV6; + // break; + // default: + // crash("Address family not supported"); + // } + + check_icmp_socket_set sockset = { + .socket4 = -1, + .socket6 = -1, + }; - int icmp_sock = socket(address_family, SOCK_RAW, icmp_proto); - if (icmp_sock == -1) { - crash("Failed to obtain ICMP socket"); - } + if (config.need_v4) { + sockset.socket4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (sockset.socket4 == -1) { + crash("Failed to obtain ICMP v4 socket"); + } - if (config.source_ip) { - set_source_ip(config.source_ip, icmp_sock); - } + if (config.source_ip) { + + struct in_addr tmp = {}; + int error_code = inet_pton(AF_INET, config.source_ip, &tmp); + if (error_code == 1) { + set_source_ip(config.source_ip, sockset.socket4, AF_INET); + } else { + // just try this mindlessly if it's not a v4 address + set_source_ip(config.source_ip, sockset.socket6, AF_INET6); + } + } #ifdef SO_TIMESTAMP - int on = 1; - if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) { - if (debug) { - printf("Warning: no SO_TIMESTAMP support\n"); + if (sockset.socket4 != -1) { + int on = 1; + if (setsockopt(sockset.socket4, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) { + if (debug) { + printf("Warning: no SO_TIMESTAMP support\n"); + } + } + } + if (sockset.socket6 != -1) { + int on = 1; + if (setsockopt(sockset.socket6, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) { + if (debug) { + printf("Warning: no SO_TIMESTAMP support\n"); + } + } } - } #endif // SO_TIMESTAMP + } + + if (config.need_v6) { + sockset.socket6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + if (sockset.socket6 == -1) { + crash("Failed to obtain ICMP v6 socket"); + } + } /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ if (setuid(getuid()) == -1) { @@ -801,8 +858,19 @@ int main(int argc, char **argv) { return 1; } - if (icmp_sock) { - int result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl)); + if (sockset.socket4) { + int result = setsockopt(sockset.socket4, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl)); + if (debug) { + if (result == -1) { + printf("setsockopt failed\n"); + } else { + printf("ttl set to %lu\n", config.ttl); + } + } + } + + if (sockset.socket6) { + int result = setsockopt(sockset.socket6, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl)); if (debug) { if (result == -1) { printf("setsockopt failed\n"); @@ -888,15 +956,22 @@ int main(int argc, char **argv) { run_checks(config.icmp_data_size, &pkt_interval, &target_interval, config.sender_id, config.mode, max_completion_time, prog_start, table, config.number_of_packets, - icmp_sock, config.number_of_targets, &program_state); + sockset, config.number_of_targets, &program_state); errno = 0; mp_check overall = mp_check_init(); - finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit, icmp_sock, + finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit, config.number_of_targets, &program_state, config.hosts, config.number_of_hosts, &overall); + if (sockset.socket4) { + close(sockset.socket4); + } + if (sockset.socket6) { + close(sockset.socket6); + } + mp_exit(overall); } @@ -904,7 +979,7 @@ static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, unsigned int *target_interval, const uint16_t sender_id, const check_icmp_execution_mode mode, const unsigned int max_completion_time, const struct timeval prog_start, ping_target **table, - const unsigned short packets, const int icmp_sock, + const unsigned short packets, const check_icmp_socket_set sockset, const unsigned short number_of_targets, check_icmp_state *program_state) { /* this loop might actually violate the pkt_interval or target_interval * settings, but only if there aren't any packets on the wire which @@ -917,20 +992,23 @@ static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, } if (table[target_index]->flags & FLAG_LOST_CAUSE) { if (debug) { - printf("%s is a lost cause. not sending any more\n", table[target_index]->name); + + char address[INET6_ADDRSTRLEN]; + parse_address(&table[target_index]->address, address, sizeof(address)); + printf("%s is a lost cause. not sending any more\n", address); } continue; } /* we're still in the game, so send next packet */ - (void)send_icmp_ping(icmp_sock, table[target_index], icmp_pkt_size, sender_id, + (void)send_icmp_ping(sockset, table[target_index], icmp_pkt_size, sender_id, program_state); /* wrap up if all targets are declared dead */ if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff(prog_start, prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { - wait_for_reply(icmp_sock, *target_interval, icmp_pkt_size, pkt_interval, + wait_for_reply(sockset, *target_interval, icmp_pkt_size, pkt_interval, target_interval, sender_id, table, packets, number_of_targets, program_state); } @@ -938,9 +1016,9 @@ static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff_to_now(prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { - wait_for_reply(icmp_sock, *pkt_interval * number_of_targets, icmp_pkt_size, - pkt_interval, target_interval, sender_id, table, packets, - number_of_targets, program_state); + wait_for_reply(sockset, *pkt_interval * number_of_targets, icmp_pkt_size, pkt_interval, + target_interval, sender_id, table, packets, number_of_targets, + program_state); } } @@ -970,7 +1048,7 @@ static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff_to_now(prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { - wait_for_reply(icmp_sock, final_wait, icmp_pkt_size, pkt_interval, target_interval, + wait_for_reply(sockset, final_wait, icmp_pkt_size, pkt_interval, target_interval, sender_id, table, packets, number_of_targets, program_state); } } @@ -986,10 +1064,11 @@ static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, * both: * icmp echo reply : the rest */ -static int wait_for_reply(int sock, const time_t time_interval, unsigned short icmp_pkt_size, - unsigned int *pkt_interval, unsigned int *target_interval, - uint16_t sender_id, ping_target **table, const unsigned short packets, - const unsigned short number_of_targets, check_icmp_state *program_state) { +static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval, + unsigned short icmp_pkt_size, unsigned int *pkt_interval, + unsigned int *target_interval, uint16_t sender_id, ping_target **table, + const unsigned short packets, const unsigned short number_of_targets, + check_icmp_state *program_state) { union icmp_packet packet; if (!(packet.buf = malloc(icmp_pkt_size))) { crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); @@ -1022,25 +1101,25 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i time_t loop_time_interval = per_pkt_wait; /* reap responses until we hit a timeout */ - ssize_t n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, - &loop_time_interval, &packet_received_timestamp); - if (!n) { + recvfrom_wto_wrapper recv_foo = + recvfrom_wto(sockset, buf, sizeof(buf), (struct sockaddr *)&resp_addr, + &loop_time_interval, &packet_received_timestamp); + if (!recv_foo.received) { if (debug > 1) { printf("recvfrom_wto() timed out during a %ld usecs wait\n", per_pkt_wait); } continue; /* timeout for this one, so keep trying */ } - if (n < 0) { + if (recv_foo.received < 0) { if (debug) { printf("recvfrom_wto() returned errors\n"); } free(packet.buf); - return (int)n; + return (int)recv_foo.received; } - // FIXME: with ipv6 we don't have an ip header here - if (address_family != AF_INET6) { + if (recv_foo.recv_proto != AF_INET6) { ip_header = (union ip_hdr *)buf; if (debug > 1) { @@ -1053,38 +1132,21 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i } } - /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ - /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ - /* alpha headers are decidedly broken. Using an ansi compiler, - * they provide ip_vhl instead of ip_hl and ip_v, so we mask - * off the bottom 4 bits */ - /* hlen = (ip->ip_vhl & 0x0f) << 2; */ - /* #else */ - int hlen = (address_family == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2; - /* #endif */ - - if (n < (hlen + ICMP_MINLEN)) { + int hlen = (recv_foo.recv_proto == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2; + + if (recv_foo.received < (hlen + ICMP_MINLEN)) { char address[INET6_ADDRSTRLEN]; parse_address(&resp_addr, address, sizeof(address)); - crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, - hlen + icmp_pkt_size, address); + crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", + recv_foo.received, hlen + icmp_pkt_size, address); } - /* else if(debug) { */ - /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */ - /* hlen, ntohs(ip->ip_len) - hlen, */ - /* sizeof(struct ip), icmp_pkt_size); */ - /* } */ - /* check the response */ - memcpy(packet.buf, buf + hlen, icmp_pkt_size); - /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr) - : sizeof(struct icmp));*/ - if ((address_family == PF_INET && + if ((recv_foo.recv_proto == AF_INET && (ntohs(packet.icp->icmp_id) != sender_id || packet.icp->icmp_type != ICMP_ECHOREPLY || ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) || - (address_family == PF_INET6 && + (recv_foo.recv_proto == AF_INET6 && (ntohs(packet.icp6->icmp6_id) != sender_id || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) { @@ -1101,7 +1163,7 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i /* this is indeed a valid response */ ping_target *target; struct icmp_ping_data data; - if (address_family == PF_INET) { + if (address_family == AF_INET) { memcpy(&data, packet.icp->icmp_data, sizeof(data)); if (debug > 2) { printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data), @@ -1171,7 +1233,7 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i char address[INET6_ADDRSTRLEN]; parse_address(&resp_addr, address, sizeof(address)); - switch (address_family) { + switch (recv_foo.recv_proto) { case AF_INET: { printf("%0.3f ms rtt from %s, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ip_header->ip.ip_ttl, @@ -1191,23 +1253,14 @@ static int wait_for_reply(int sock, const time_t time_interval, unsigned short i } /* the ping functions */ -static int send_icmp_ping(const int sock, ping_target *host, const unsigned short icmp_pkt_size, - const uint16_t sender_id, check_icmp_state *program_state) { - if (sock == -1) { - errno = 0; - crash("Attempt to send on bogus socket"); - return -1; - } - - void *buf = NULL; - +static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host, + const unsigned short icmp_pkt_size, const uint16_t sender_id, + check_icmp_state *program_state) { + void *buf = calloc(1, icmp_pkt_size); if (!buf) { - if (!(buf = malloc(icmp_pkt_size))) { - crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); - return -1; /* might be reached if we're in debug mode */ - } + crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); + return -1; /* might be reached if we're in debug mode */ } - memset(buf, 0, icmp_pkt_size); struct timeval current_time; if ((gettimeofday(¤t_time, NULL)) == -1) { @@ -1221,7 +1274,7 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor socklen_t addrlen; - if (address_family == AF_INET) { + if (host->address.ss_family == AF_INET) { struct icmp *icp = (struct icmp *)buf; addrlen = sizeof(struct sockaddr_in); @@ -1235,11 +1288,14 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); if (debug > 2) { + char address[INET6_ADDRSTRLEN]; + parse_address((&host->address), address, sizeof(address)); + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, - host->name); + address); } - } else { + } else if (host->address.ss_family == AF_INET6) { struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; addrlen = sizeof(struct sockaddr_in6); @@ -1253,10 +1309,16 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor // let checksum be calculated automatically if (debug > 2) { - printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", + char address[INET6_ADDRSTRLEN]; + parse_address((&host->address), address, sizeof(address)); + + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to target %s\n", sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, - host->name); + address); } + } else { + // unknown address family + crash("unknown address family in ", __func__); } struct iovec iov; @@ -1266,7 +1328,7 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor struct msghdr hdr; memset(&hdr, 0, sizeof(hdr)); - hdr.msg_name = (struct sockaddr *)&host->saddr_in; + hdr.msg_name = (struct sockaddr *)&host->address; hdr.msg_namelen = addrlen; hdr.msg_iov = &iov; hdr.msg_iovlen = 1; @@ -1274,19 +1336,29 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor errno = 0; long int len; -/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ + /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ + if (host->address.ss_family == AF_INET) { #ifdef MSG_CONFIRM - len = sendmsg(sock, &hdr, MSG_CONFIRM); + len = sendmsg(sockset.socket4, &hdr, MSG_CONFIRM); #else - len = sendmsg(sock, &hdr, 0); + len = sendmsg(sockset.socket4, &hdr, 0); #endif + } else if (host->address.ss_family == AF_INET6) { +#ifdef MSG_CONFIRM + len = sendmsg(sockset.socket6, &hdr, MSG_CONFIRM); +#else + len = sendmsg(sockset.socket6, &hdr, 0); +#endif + } else { + assert(false); + } free(buf); if (len < 0 || (unsigned int)len != icmp_pkt_size) { if (debug) { char address[INET6_ADDRSTRLEN]; - parse_address((&host->saddr_in), address, sizeof(address)); + parse_address((&host->address), address, sizeof(address)); printf("Failed to send ping to %s: %s\n", address, strerror(errno)); } errno = 0; @@ -1299,9 +1371,9 @@ static int send_icmp_ping(const int sock, ping_target *host, const unsigned shor return 0; } -static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, - struct sockaddr *saddr, time_t *timeout, - struct timeval *received_timestamp) { +static recvfrom_wto_wrapper recvfrom_wto(const check_icmp_socket_set sockset, void *buf, + const unsigned int len, struct sockaddr *saddr, + time_t *timeout, struct timeval *received_timestamp) { #ifdef HAVE_MSGHDR_MSG_CONTROL char ans_data[4096]; #endif // HAVE_MSGHDR_MSG_CONTROL @@ -1309,11 +1381,16 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, struct cmsghdr *chdr; #endif + recvfrom_wto_wrapper result = { + .received = 0, + .recv_proto = AF_UNSPEC, + }; + if (!*timeout) { if (debug) { printf("*timeout is not\n"); } - return 0; + return result; } struct timeval real_timeout; @@ -1327,13 +1404,21 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, // Read fds for select with the socket fd_set read_fds; FD_ZERO(&read_fds); - FD_SET(sock, &read_fds); + + if (sockset.socket4 != -1) { + FD_SET(sockset.socket4, &read_fds); + } + if (sockset.socket6 != -1) { + FD_SET(sockset.socket6, &read_fds); + } + + int nfds = (sockset.socket4 > sockset.socket6 ? sockset.socket4 : sockset.socket6) + 1; struct timeval then; gettimeofday(&then, NULL); errno = 0; - int select_return = select(sock + 1, &read_fds, &dummy_write_fds, NULL, &real_timeout); + int select_return = select(nfds, &read_fds, &dummy_write_fds, NULL, &real_timeout); if (select_return < 0) { crash("select() in recvfrom_wto"); } @@ -1343,7 +1428,7 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, *timeout = get_timevaldiff(then, now); if (!select_return) { - return 0; /* timeout */ + return result; /* timeout */ } unsigned int slen = sizeof(struct sockaddr_storage); @@ -1364,7 +1449,18 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, #endif }; - ssize_t ret = recvmsg(sock, &hdr, 0); + ssize_t ret; + if (FD_ISSET(sockset.socket4, &read_fds)) { + ret = recvmsg(sockset.socket4, &hdr, 0); + result.recv_proto = AF_INET; + } else if (FD_ISSET(sockset.socket6, &read_fds)) { + ret = recvmsg(sockset.socket6, &hdr, 0); + result.recv_proto = AF_INET6; + } else { + assert(false); + } + + result.received = ret; #ifdef SO_TIMESTAMP for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { @@ -1382,11 +1478,11 @@ static ssize_t recvfrom_wto(const int sock, void *buf, const unsigned int len, gettimeofday(tv, NULL); #endif // SO_TIMESTAMP - return (ret); + return (result); } static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, - check_icmp_threshold warn, check_icmp_threshold crit, const int icmp_sock, + check_icmp_threshold warn, check_icmp_threshold crit, const unsigned short number_of_targets, check_icmp_state *program_state, check_icmp_target_container host_list[], unsigned short number_of_hosts, mp_check overall[static 1]) { @@ -1397,10 +1493,6 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, printf("finish(%d) called\n", sig); } - if (icmp_sock != -1) { - close(icmp_sock); - } - if (debug) { printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", program_state->icmp_sent, program_state->icmp_recv, program_state->icmp_lost); @@ -1476,17 +1568,21 @@ static time_t get_timevaldiff_to_now(struct timeval earlier) { } static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) { + assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6)); + if (debug) { char straddr[INET6_ADDRSTRLEN]; - parse_address((struct sockaddr_storage *)&address, straddr, sizeof(straddr)); + parse_address((&address), straddr, sizeof(straddr)); printf("add_target_ip called with: %s\n", straddr); } struct sockaddr_in *sin; struct sockaddr_in6 *sin6; - if (address_family == AF_INET) { + if (address.ss_family == AF_INET) { sin = (struct sockaddr_in *)&address; - } else { + } else if (address.ss_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)&address; + } else { + assert(false); } add_target_ip_wrapper result = { @@ -1496,9 +1592,9 @@ static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) { /* disregard obviously stupid addresses * (I didn't find an ipv6 equivalent to INADDR_NONE) */ - if (((address_family == AF_INET && + if (((address.ss_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || - (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { + (address.ss_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { result.error_code = ERROR; return result; } @@ -1513,32 +1609,21 @@ static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) { crash("add_target_ip(%s): malloc(%lu) failed", straddr, sizeof(ping_target)); } - *target = ping_target_init(); + ping_target_create_wrapper target_wrapper = ping_target_create(address); - /* set the values. use calling name for output */ - target->name = strdup(straddr); - - /* fill out the sockaddr_storage struct */ - struct sockaddr_in *host_sin; - struct sockaddr_in6 *host_sin6; - if (address_family == AF_INET) { - host_sin = (struct sockaddr_in *)&target->saddr_in; - host_sin->sin_family = AF_INET; - host_sin->sin_addr.s_addr = sin->sin_addr.s_addr; + if (target_wrapper.errorcode == OK) { + *target = target_wrapper.host; + result.target = target; } else { - host_sin6 = (struct sockaddr_in6 *)&target->saddr_in; - host_sin6->sin6_family = AF_INET6; - memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, - sizeof host_sin6->sin6_addr.s6_addr); + result.error_code = target_wrapper.errorcode; } - result.target = target; - return result; } /* wrapper for add_target_ip */ -static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode) { +static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode, + sa_family_t enforced_proto) { if (debug > 0) { printf("add_target called with argument %s\n", arg); } @@ -1548,30 +1633,31 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode struct sockaddr_in6 *sin6 = NULL; int error_code = -1; - switch (address_family) { - case -1: - /* -4 and -6 are not specified on cmdline */ - address_family = AF_INET; + switch (enforced_proto) { + case AF_UNSPEC: + /* + * no enforced protocoll family + * try to parse the address with each one + */ sin = (struct sockaddr_in *)&address_storage; - error_code = inet_pton(address_family, arg, &sin->sin_addr); + error_code = inet_pton(AF_INET, arg, &sin->sin_addr); + address_storage.ss_family = AF_INET; if (error_code != 1) { - address_family = AF_INET6; sin6 = (struct sockaddr_in6 *)&address_storage; - error_code = inet_pton(address_family, arg, &sin6->sin6_addr); - } - /* If we don't find any valid addresses, we still don't know the address_family */ - if (error_code != 1) { - address_family = -1; + error_code = inet_pton(AF_INET6, arg, &sin6->sin6_addr); + address_storage.ss_family = AF_INET6; } break; case AF_INET: sin = (struct sockaddr_in *)&address_storage; - error_code = inet_pton(address_family, arg, &sin->sin_addr); + error_code = inet_pton(AF_INET, arg, &sin->sin_addr); + address_storage.ss_family = AF_INET; break; case AF_INET6: sin6 = (struct sockaddr_in6 *)&address_storage; - error_code = inet_pton(address_family, arg, &sin6->sin6_addr); + error_code = inet_pton(AF_INET, arg, &sin6->sin6_addr); + address_storage.ss_family = AF_INET6; break; default: crash("Address family not supported"); @@ -1580,9 +1666,11 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode add_target_wrapper result = { .error_code = OK, .targets = NULL, + .has_v4 = false, + .has_v6 = false, }; - /* don't resolve if we don't have to */ + // if error_code == 1 the address was a valid address parsed above if (error_code == 1) { /* don't add all ip's if we were given a specific one */ add_target_ip_wrapper targeted = add_target_ip(address_storage); @@ -1592,19 +1680,21 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode return result; } + if (targeted.target->address.ss_family == AF_INET) { + result.has_v4 = true; + } else if (targeted.target->address.ss_family == AF_INET6) { + result.has_v6 = true; + } else { + assert(false); + } result.targets = targeted.target; result.number_of_targets = 1; return result; } - struct addrinfo hints; + struct addrinfo hints = {}; errno = 0; - memset(&hints, 0, sizeof(hints)); - if (address_family == -1) { - hints.ai_family = AF_UNSPEC; - } else { - hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6; - } + hints.ai_family = enforced_proto; hints.ai_socktype = SOCK_RAW; int error; @@ -1615,7 +1705,6 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode result.error_code = ERROR; return result; } - address_family = res->ai_family; /* possibly add all the IP's as targets */ for (struct addrinfo *address = res; address != NULL; address = address->ai_next) { @@ -1634,6 +1723,11 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode } else { result.number_of_targets += ping_target_list_append(result.targets, tmp.target); } + if (address->ai_family == AF_INET) { + result.has_v4 = true; + } else if (address->ai_family == AF_INET6) { + result.has_v6 = true; + } } /* this is silly, but it works */ @@ -1652,11 +1746,11 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode return result; } -static void set_source_ip(char *arg, const int icmp_sock) { +static void set_source_ip(char *arg, const int icmp_sock, sa_family_t addr_family) { struct sockaddr_in src; memset(&src, 0, sizeof(src)); - src.sin_family = address_family; + src.sin_family = addr_family; if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) { src.sin_addr.s_addr = get_ip_address(arg); } @@ -2026,7 +2120,8 @@ void print_usage(void) { printf(" %s [options] [-H] host1 host2 hostN\n", progname); } -static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode) { +static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode, + sa_family_t enforced_proto) { if (debug) { printf("add_host called with argument %s\n", arg); } @@ -2034,15 +2129,20 @@ static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode) { add_host_wrapper result = { .error_code = OK, .host = check_icmp_target_container_init(), + .has_v4 = false, + .has_v6 = false, }; - add_target_wrapper targets = add_target(arg, mode); + add_target_wrapper targets = add_target(arg, mode, enforced_proto); if (targets.error_code != OK) { result.error_code = targets.error_code; return result; } + result.has_v4 = targets.has_v4; + result.has_v6 = targets.has_v6; + result.host = check_icmp_target_container_init(); result.host.name = strdup(arg); @@ -2063,7 +2163,12 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, mp_subcheck result = mp_subcheck_init(); result = mp_set_subcheck_default_state(result, STATE_OK); - xasprintf(&result.output, "%s", target.name); + + char address[INET6_ADDRSTRLEN]; + memset(address, 0, INET6_ADDRSTRLEN); + parse_address(&target.address, address, sizeof(address)); + + xasprintf(&result.output, "%s", address); double packet_loss; double rta; @@ -2076,8 +2181,6 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, /* up the down counter if not already counted */ if (target.flags & FLAG_LOST_CAUSE) { - char address[INET6_ADDRSTRLEN]; - parse_address(&target.error_addr, address, sizeof(address)); xasprintf(&result.output, "%s: %s @ %s", result.output, get_icmp_error_msg(target.icmp_type, target.icmp_code), address); } else { /* not marked as lost cause, so we have no flags for it */ @@ -2159,7 +2262,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (packet_loss < 100) { mp_perfdata pd_rta = perfdata_init(); - xasprintf(&pd_rta.label, "%srta", target.name); + xasprintf(&pd_rta.label, "%srta", address); pd_rta.uom = strdup("ms"); pd_rta.value = mp_create_pd_value(rta / 1000); pd_rta.min = mp_create_pd_value(0); @@ -2169,13 +2272,13 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, mp_add_perfdata_to_subcheck(&sc_rta, pd_rta); mp_perfdata pd_rt_min = perfdata_init(); - xasprintf(&pd_rt_min.label, "%srtmin", target.name); + xasprintf(&pd_rt_min.label, "%srtmin", address); pd_rt_min.value = mp_create_pd_value(target.rtmin / 1000); pd_rt_min.uom = strdup("ms"); mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_min); mp_perfdata pd_rt_max = perfdata_init(); - xasprintf(&pd_rt_max.label, "%srtmax", target.name); + xasprintf(&pd_rt_max.label, "%srtmax", address); pd_rt_max.value = mp_create_pd_value(target.rtmax / 1000); pd_rt_max.uom = strdup("ms"); mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_max); @@ -2198,7 +2301,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, } mp_perfdata pd_pl = perfdata_init(); - xasprintf(&pd_pl.label, "%spl", target.name); + xasprintf(&pd_pl.label, "%spl", address); pd_pl.uom = strdup("%"); pd_pl.warn = mp_range_set_end(pd_pl.warn, mp_create_pd_value(warn.pl)); @@ -2226,7 +2329,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (packet_loss < 100) { mp_perfdata pd_jitter = perfdata_init(); pd_jitter.uom = strdup("ms"); - xasprintf(&pd_jitter.label, "%sjitter_avg", target.name); + xasprintf(&pd_jitter.label, "%sjitter_avg", address); pd_jitter.value = mp_create_pd_value(target.jitter); pd_jitter.warn = mp_range_set_end(pd_jitter.warn, mp_create_pd_value(warn.jitter)); pd_jitter.crit = mp_range_set_end(pd_jitter.crit, mp_create_pd_value(crit.jitter)); @@ -2234,13 +2337,13 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, mp_perfdata pd_jitter_min = perfdata_init(); pd_jitter_min.uom = strdup("ms"); - xasprintf(&pd_jitter_min.label, "%sjitter_min", target.name); + xasprintf(&pd_jitter_min.label, "%sjitter_min", address); pd_jitter_min.value = mp_create_pd_value(target.jitter_min); mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_min); mp_perfdata pd_jitter_max = perfdata_init(); pd_jitter_max.uom = strdup("ms"); - xasprintf(&pd_jitter_max.label, "%sjitter_max", target.name); + xasprintf(&pd_jitter_max.label, "%sjitter_max", address); pd_jitter_max.value = mp_create_pd_value(target.jitter_max); mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_max); } @@ -2262,7 +2365,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (packet_loss < 100) { mp_perfdata pd_mos = perfdata_init(); - xasprintf(&pd_mos.label, "%smos", target.name); + xasprintf(&pd_mos.label, "%smos", address); pd_mos.value = mp_create_pd_value(mos); pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos)); pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos)); @@ -2288,7 +2391,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (packet_loss < 100) { mp_perfdata pd_score = perfdata_init(); - xasprintf(&pd_score.label, "%sscore", target.name); + xasprintf(&pd_score.label, "%sscore", address); pd_score.value = mp_create_pd_value(score); pd_score.warn = mp_range_set_end(pd_score.warn, mp_create_pd_value(warn.score)); pd_score.crit = mp_range_set_end(pd_score.crit, mp_create_pd_value(crit.score)); diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index a2b54c6a..47604952 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -1,7 +1,7 @@ #include "./config.h" -#include "states.h" #include #include +#include #include "./check_icmp_helpers.h" #include "../../plugins/netutils.h" @@ -38,7 +38,10 @@ check_icmp_config check_icmp_config_init() { .pkt_interval = DEFAULT_PKT_INTERVAL, .target_interval = 0, .number_of_packets = DEFAULT_NUMBER_OF_PACKETS, + .source_ip = NULL, + .need_v4 = false, + .need_v6 = false, .sender_id = {}, @@ -71,68 +74,31 @@ check_icmp_state check_icmp_state_init() { return tmp; } -ping_target_create_wrapper ping_target_create(char *name, struct sockaddr_storage *address) { - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - if (address_family == AF_INET) { - sin = (struct sockaddr_in *)address; - } else { - sin6 = (struct sockaddr_in6 *)address; - } - +ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) { ping_target_create_wrapper result = { .errorcode = OK, }; + struct sockaddr_storage *tmp_addr = &address; + /* disregard obviously stupid addresses * (I didn't find an ipv6 equivalent to INADDR_NONE) */ - if (((address_family == AF_INET && - (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || - (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { + if (((tmp_addr->ss_family == AF_INET && + (((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_NONE || + ((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_ANY))) || + (tmp_addr->ss_family == AF_INET6 && + (((struct sockaddr_in6 *)tmp_addr)->sin6_addr.s6_addr == in6addr_any.s6_addr))) { result.errorcode = ERROR; return result; } - // TODO: Maybe add the following back in as a sanity check for the config - // /* no point in adding two identical IP's, so don't. ;) */ - // struct sockaddr_in *host_sin; - // struct sockaddr_in6 *host_sin6; - // struct rta_host *host = host_list; - - // while (host) { - // host_sin = (struct sockaddr_in *)&host->saddr_in; - // host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; - - // if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) || - // (address_family == AF_INET6 && - // host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { - // if (debug) { - // printf("Identical IP already exists. Not adding %s\n", name); - // } - // return -1; - // } - // host = host->next; - // } - /* add the fresh ip */ - ping_target host = ping_target_init(); - - /* set the values. use calling name for output */ - host.name = strdup(name); + ping_target target = ping_target_init(); /* fill out the sockaddr_storage struct */ - if (address_family == AF_INET) { - struct sockaddr_in *host_sin = (struct sockaddr_in *)&host.saddr_in; - host_sin->sin_family = AF_INET; - host_sin->sin_addr.s_addr = sin->sin_addr.s_addr; - } else { - struct sockaddr_in6 *host_sin6 = (struct sockaddr_in6 *)&host.saddr_in; - host_sin6->sin6_family = AF_INET6; - memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, - sizeof host_sin6->sin6_addr.s6_addr); - } + target.address = address; - result.host = host; + result.host = target; return result; } diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h index 5f771635..713dd8ce 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.h +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -10,10 +10,10 @@ #include typedef struct rta_host { - unsigned short id; /* id in **table, and icmp pkts */ - char *name; /* arg used for adding this host */ - char *msg; /* icmp error message, if any */ - struct sockaddr_storage saddr_in; /* the address of this host */ + unsigned short id; /* id in **table, and icmp pkts */ + char *msg; /* icmp error message, if any */ + + struct sockaddr_storage address; /* the address of this host */ struct sockaddr_storage error_addr; /* stores address of error replies */ time_t time_waited; /* total time waited, in usecs */ unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ @@ -59,7 +59,12 @@ typedef struct { ping_target host; } ping_target_create_wrapper; -ping_target_create_wrapper ping_target_create(char *name, struct sockaddr_storage *address); +typedef struct { + int socket4; + int socket6; +} check_icmp_socket_set; + +ping_target_create_wrapper ping_target_create(struct sockaddr_storage address); unsigned int ping_target_list_append(ping_target *list, ping_target *elem); void check_icmp_timeout_handler(int, siginfo_t *, void *); diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index aa33c991..8e9c96b2 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -63,7 +63,10 @@ typedef struct { unsigned int pkt_interval; unsigned int target_interval; unsigned short number_of_packets; + char *source_ip; + bool need_v4; + bool need_v6; uint16_t sender_id; // PID of the main process, which is used as an ID in packets -- cgit v1.2.3-74-g34f1 From d92bbaef8386e77c00ecdeab9fc722fa4b9106b0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 18:46:30 +0200 Subject: Some more fixes --- plugins-root/check_icmp.c | 53 +++++++++++++------------- plugins-root/check_icmp.d/check_icmp_helpers.c | 2 +- plugins-root/check_icmp.d/check_icmp_helpers.h | 4 +- plugins-root/check_icmp.d/config.h | 4 +- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index d4e55b0d..43eae276 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -147,8 +147,8 @@ static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family); /* Receiving data */ static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval, - unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, uint16_t sender_id, ping_target **table, + unsigned short icmp_pkt_size, time_t *pkt_interval, + time_t *target_interval, uint16_t sender_id, ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); @@ -160,12 +160,12 @@ static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *bu struct sockaddr *saddr, time_t *timeout, struct timeval *received_timestamp); static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, - unsigned int *pkt_interval, unsigned int *target_interval, - uint16_t sender_id, ping_target **table, unsigned short packets, + time_t *pkt_interval, time_t *target_interval, uint16_t sender_id, + ping_target **table, unsigned short packets, unsigned short number_of_targets, check_icmp_state *program_state); /* Sending data */ -static int send_icmp_ping(check_icmp_socket_set socket, ping_target *host, +static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host, unsigned short icmp_pkt_size, uint16_t sender_id, check_icmp_state *program_state); @@ -194,12 +194,11 @@ static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_s threshold_mode mode); /* main test function */ -static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, uint16_t sender_id, - check_icmp_execution_mode mode, unsigned int max_completion_time, - struct timeval prog_start, ping_target **table, unsigned short packets, - check_icmp_socket_set sockset, unsigned short number_of_targets, - check_icmp_state *program_state); +static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval, + uint16_t sender_id, check_icmp_execution_mode mode, + unsigned int max_completion_time, struct timeval prog_start, + ping_target **table, unsigned short packets, check_icmp_socket_set sockset, + unsigned short number_of_targets, check_icmp_state *program_state); mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, check_icmp_threshold warn, check_icmp_threshold crit); @@ -683,7 +682,7 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm } static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, - unsigned int *pkt_interval, unsigned int *target_interval, + time_t *pkt_interval, time_t *target_interval, const uint16_t sender_id, ping_target **table, unsigned short packets, const unsigned short number_of_targets, check_icmp_state *program_state) { @@ -899,7 +898,7 @@ int main(int argc, char **argv) { struct timeval prog_start; gettimeofday(&prog_start, NULL); - unsigned int max_completion_time = + time_t max_completion_time = ((config.number_of_targets * config.number_of_packets * config.pkt_interval) + (config.number_of_targets * config.target_interval)) + (config.number_of_targets * config.number_of_packets * config.crit.rta) + config.crit.rta; @@ -916,15 +915,15 @@ int main(int argc, char **argv) { if (debug) { if (max_completion_time > (timeout * 1000000)) { - printf("max_completion_time: %u timeout: %u\n", max_completion_time, timeout); - printf("Timeout must be at least %u\n", (max_completion_time / 1000000) + 1); + printf("max_completion_time: %ld timeout: %u\n", max_completion_time, timeout); + printf("Timeout must be at least %ld\n", (max_completion_time / 1000000) + 1); } } if (debug) { printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", config.crit.rta, config.crit.pl, config.warn.rta, config.warn.pl); - printf("pkt_interval: %u target_interval: %u\n", config.pkt_interval, + printf("pkt_interval: %ld target_interval: %ld\n", config.pkt_interval, config.target_interval); printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_pkt_size, timeout); } @@ -949,8 +948,8 @@ int main(int argc, char **argv) { target_index++; } - unsigned int pkt_interval = config.pkt_interval; - unsigned int target_interval = config.target_interval; + time_t pkt_interval = config.pkt_interval; + time_t target_interval = config.target_interval; check_icmp_state program_state = check_icmp_state_init(); @@ -975,12 +974,12 @@ int main(int argc, char **argv) { mp_exit(overall); } -static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, const uint16_t sender_id, - const check_icmp_execution_mode mode, const unsigned int max_completion_time, - const struct timeval prog_start, ping_target **table, - const unsigned short packets, const check_icmp_socket_set sockset, - const unsigned short number_of_targets, check_icmp_state *program_state) { +static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval, + const uint16_t sender_id, const check_icmp_execution_mode mode, + const unsigned int max_completion_time, const struct timeval prog_start, + ping_target **table, const unsigned short packets, + const check_icmp_socket_set sockset, const unsigned short number_of_targets, + check_icmp_state *program_state) { /* this loop might actually violate the pkt_interval or target_interval * settings, but only if there aren't any packets on the wire which * indicates that the target can handle an increased packet rate */ @@ -1065,8 +1064,8 @@ static void run_checks(unsigned short icmp_pkt_size, unsigned int *pkt_interval, * icmp echo reply : the rest */ static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval, - unsigned short icmp_pkt_size, unsigned int *pkt_interval, - unsigned int *target_interval, uint16_t sender_id, ping_target **table, + unsigned short icmp_pkt_size, time_t *pkt_interval, + time_t *target_interval, uint16_t sender_id, ping_target **table, const unsigned short packets, const unsigned short number_of_targets, check_icmp_state *program_state) { union icmp_packet packet; @@ -1568,7 +1567,7 @@ static time_t get_timevaldiff_to_now(struct timeval earlier) { } static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) { - assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6)); + assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6)); if (debug) { char straddr[INET6_ADDRSTRLEN]; diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 47604952..7a936cc9 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -43,7 +43,7 @@ check_icmp_config check_icmp_config_init() { .need_v4 = false, .need_v6 = false, - .sender_id = {}, + .sender_id = 0, .mode = MODE_RTA, diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h index 713dd8ce..1b9372ce 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.h +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -9,7 +9,7 @@ #include #include -typedef struct rta_host { +typedef struct ping_target { unsigned short id; /* id in **table, and icmp pkts */ char *msg; /* icmp error message, if any */ @@ -32,7 +32,7 @@ typedef struct rta_host { bool found_out_of_order_packets; - struct rta_host *next; + struct ping_target *next; } ping_target; ping_target ping_target_init(); diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index 8e9c96b2..97be7fc1 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -60,8 +60,8 @@ typedef struct { unsigned long ttl; unsigned short icmp_data_size; unsigned short icmp_pkt_size; - unsigned int pkt_interval; - unsigned int target_interval; + time_t pkt_interval; + time_t target_interval; unsigned short number_of_packets; char *source_ip; -- cgit v1.2.3-74-g34f1 From 3b76fdc8405c4bd9ee5b1d798831a44e811c2fd6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 18:48:49 +0200 Subject: Fix typos --- plugins-root/check_icmp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 43eae276..937bc102 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -211,7 +211,7 @@ evaluate_host_wrapper evaluate_host(check_icmp_target_container host, check_icmp_mode_switches modes, check_icmp_threshold warn, check_icmp_threshold crit); -/* Target aquisition */ +/* Target acquisition */ typedef struct { int error_code; check_icmp_target_container host; @@ -1528,7 +1528,6 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, mp_subcheck sc_min_targets_alive = mp_subcheck_init(); sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK); - // TODO problably broken now if (targets_ok >= min_hosts_alive) { // TODO this should overwrite the main state sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK); @@ -1635,7 +1634,7 @@ static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode switch (enforced_proto) { case AF_UNSPEC: /* - * no enforced protocoll family + * no enforced protocol family * try to parse the address with each one */ sin = (struct sockaddr_in *)&address_storage; -- cgit v1.2.3-74-g34f1 From 738d58714d3845252e210e342e19ae2cec02c5a6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 19:11:14 +0200 Subject: some more fixes --- plugins-root/check_icmp.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 937bc102..139aeff3 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -249,7 +249,7 @@ static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive mp_check overall[static 1]); /* Error exit */ -static void crash(const char *fmt, ...); +static void crash(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); /** global variables **/ static int debug = 0; @@ -575,7 +575,7 @@ static void crash(const char *fmt, ...) { puts(""); exit(3); -} +} static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code) { const char *msg = "unreachable"; @@ -899,8 +899,8 @@ int main(int argc, char **argv) { gettimeofday(&prog_start, NULL); time_t max_completion_time = - ((config.number_of_targets * config.number_of_packets * config.pkt_interval) + - (config.number_of_targets * config.target_interval)) + + ((config.pkt_interval *config.number_of_targets * config.number_of_packets) + + (config.target_interval * config.number_of_targets)) + (config.number_of_targets * config.number_of_packets * config.crit.rta) + config.crit.rta; if (debug) { @@ -1136,8 +1136,9 @@ static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_inter if (recv_foo.received < (hlen + ICMP_MINLEN)) { char address[INET6_ADDRSTRLEN]; parse_address(&resp_addr, address, sizeof(address)); - crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", + crash("received packet too short for ICMP (%ld bytes, expected %d) from %s\n", recv_foo.received, hlen + icmp_pkt_size, address); + } /* check the response */ memcpy(packet.buf, buf + hlen, icmp_pkt_size); @@ -1271,7 +1272,7 @@ static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host data.ping_id = 10; /* host->icmp.icmp_sent; */ memcpy(&data.stime, ¤t_time, sizeof(current_time)); - socklen_t addrlen; + socklen_t addrlen = 0; if (host->address.ss_family == AF_INET) { struct icmp *icp = (struct icmp *)buf; @@ -1317,7 +1318,7 @@ static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host } } else { // unknown address family - crash("unknown address family in ", __func__); + crash("unknown address family in %s", __func__); } struct iovec iov; -- cgit v1.2.3-74-g34f1 From 906e895c72fdb29c2ccb1d9665ae416c6e005ae4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 19:19:07 +0200 Subject: Format specifier fixes --- plugins-root/check_icmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 139aeff3..a57edef4 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -1272,7 +1272,7 @@ static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host data.ping_id = 10; /* host->icmp.icmp_sent; */ memcpy(&data.stime, ¤t_time, sizeof(current_time)); - socklen_t addrlen = 0; + socklen_t addrlen; if (host->address.ss_family == AF_INET) { struct icmp *icp = (struct icmp *)buf; @@ -2253,10 +2253,10 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (rta >= crit.rta) { sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL); - xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, crit.rta / 1000); + xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double) crit.rta / 1000); } else if (rta >= warn.rta) { sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING); - xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, warn.rta / 1000); + xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double) warn.rta / 1000); } if (packet_loss < 100) { @@ -2378,14 +2378,14 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (modes.score_mode) { mp_subcheck sc_score = mp_subcheck_init(); sc_score = mp_set_subcheck_default_state(sc_score, STATE_OK); - xasprintf(&sc_score.output, "Score %u", score); + xasprintf(&sc_score.output, "Score %f", score); if (score <= crit.score) { sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL); - xasprintf(&sc_score.output, "%s < %u", sc_score.output, crit.score); + xasprintf(&sc_score.output, "%s < %f", sc_score.output, crit.score); } else if (score <= warn.score) { sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING); - xasprintf(&sc_score.output, "%s < %u", sc_score.output, warn.score); + xasprintf(&sc_score.output, "%s < %f", sc_score.output, warn.score); } if (packet_loss < 100) { -- cgit v1.2.3-74-g34f1 From dd93b1403ad28064b0d444189d8aa938dc25365d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 18 May 2025 19:19:23 +0200 Subject: utils: Make fmt function for compiler --- plugins/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/utils.h b/plugins/utils.h index 92a6c115..1d3c153c 100644 --- a/plugins/utils.h +++ b/plugins/utils.h @@ -76,7 +76,7 @@ char *strnl(char *); char *strpcpy(char *, const char *, const char *); char *strpcat(char *, const char *, const char *); int xvasprintf(char **strp, const char *fmt, va_list ap); -int xasprintf(char **strp, const char *fmt, ...); +int xasprintf(char **strp, const char *fmt, ...)__attribute__ ((format (printf, 2, 3))); void usage(const char *) __attribute__((noreturn)); void usage2(const char *, const char *) __attribute__((noreturn)); -- cgit v1.2.3-74-g34f1 From 0f8690c19b352549961c46c75c7307814e255db0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 9 Jun 2025 12:33:15 +0200 Subject: Remove check_icmp text regex tests --- plugins-root/t/check_icmp.t | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t index de1d88d2..2c1d12e6 100644 --- a/plugins-root/t/check_icmp.t +++ b/plugins-root/t/check_icmp.t @@ -18,9 +18,6 @@ if ($allow_sudo eq "yes" or $> == 0) { } my $sudo = $> == 0 ? '' : 'sudo'; -my $successOutput = '/OK - .*? rta (?:[\d\.]+ms)|(?:nan), lost \d+%/'; -my $failureOutput = '/(WARNING|CRITICAL) - .*? rta (?:[\d\.]+ms > [\d\.]+ms|nan)/'; - my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE", "The hostname of system responsive to network requests", "localhost" ); @@ -39,105 +36,82 @@ $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -w 10000ms,100% -c 10000ms,100%" ); is( $res->return_code, 0, "Syntax ok" ); -like( $res->output, $successOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 10000ms,100%" ); is( $res->return_code, 1, "Syntax ok, with forced warning" ); -like( $res->output, $failureOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%" ); is( $res->return_code, 2, "Syntax ok, with forced critical" ); -like( $res->output, $failureOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -t 2" ); is( $res->return_code, 2, "Timeout - host nonresponsive" ); -like( $res->output, '/pl=100%/', "Error contains 'pl=100%' string (for 100% packet loss)" ); -like( $res->output, '/rta=U/', "Error contains 'rta=U' string" ); $res = NPTest->testCmd( "$sudo ./check_icmp -w 10000ms,100% -c 10000ms,100%" ); is( $res->return_code, 3, "No hostname" ); -like( $res->output, '/No hosts to check/', "Output with appropriate error message"); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 0 -t 2" ); is( $res->return_code, 0, "One host nonresponsive - zero required" ); -like( $res->output, $successOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 1 -t 2" ); is( $res->return_code, 0, "One of two host nonresponsive - one required" ); -like( $res->output, $successOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" ); is( $res->return_code, 2, "One of two host nonresponsive - two required" ); -like( $res->output, $failureOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" ); is( $res->return_code, 0, "IPv4 source_ip accepted" ); -like( $res->output, $successOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -b 65507" ); is( $res->return_code, 0, "Try max packet size" ); -like( $res->output, $successOutput, "Output OK - Didn't overflow" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1 -t 2" ); is( $res->return_code, 0, "rta works" ); -like( $res->output, $successOutput, "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1 -t 2" ); is( $res->return_code, 0, "pl works" ); -like( $res->output, '/lost 0%/', "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -J 80,90 -t 2" ); is( $res->return_code, 0, "jitter works" ); -like( $res->output, '/jitter \d/', "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -M 4,3 -t 2" ); is( $res->return_code, 0, "mos works" ); -like( $res->output, '/MOS \d/', "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -S 80,70 -t 2" ); is( $res->return_code, 0, "score works" ); -like( $res->output, '/Score \d/', "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -O -t 2" ); is( $res->return_code, 0, "order works" ); -like( $res->output, '/Packets in order/', "Output OK" ); $res = NPTest->testCmd( "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100 -t 2" ); is( $res->return_code, 0, "order works" ); -like( $res->output, '/Packets in order/', "Output OK" ); -like( $res->output, '/Score \d/', "Output OK" ); -like( $res->output, '/MOS \d/', "Output OK" ); -like( $res->output, '/jitter \d/', "Output OK" ); -like( $res->output, '/lost 0%/', "Output OK" ); -like( $res->output, $successOutput, "Output OK" ); -- cgit v1.2.3-74-g34f1 From 1187374e745af310a2e346ab1df5250d07c65a69 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Wed, 11 Jun 2025 13:11:04 +0200 Subject: Remove unused FPING6 variable --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index bd3de196..ae704361 100644 --- a/configure.ac +++ b/configure.ac @@ -1519,7 +1519,6 @@ then fi AC_PATH_PROG(PATH_TO_FPING,fping) -AC_PATH_PROG(PATH_TO_FPING6,fping6) AC_ARG_WITH(fping_command, ACX_HELP_STRING([--with-fping-command=PATH], -- cgit v1.2.3-74-g34f1 From 88683af1da8baae6b252795ab5d85a48e9cd3e63 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Wed, 11 Jun 2025 17:09:27 +0200 Subject: Implement autoconf logic for fping version detection --- configure.ac | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index ae704361..bec50cb4 100644 --- a/configure.ac +++ b/configure.ac @@ -1523,10 +1523,44 @@ AC_PATH_PROG(PATH_TO_FPING,fping) AC_ARG_WITH(fping_command, ACX_HELP_STRING([--with-fping-command=PATH], [Path to fping command]), PATH_TO_FPING=$withval) -if test -n "$PATH_TO_FPING" -then +if test -n "$PATH_TO_FPING"; then AC_DEFINE_UNQUOTED(PATH_TO_FPING,"$PATH_TO_FPING",[path to fping]) EXTRAS="$EXTRAS check_fping\$(EXEEXT)" + + if test -z "$($PATH_TO_FPING --version)" ; then + AC_MSG_NOTICE([failed to get version of fping]) + else + FPING_MAJOR_VERSION="$($PATH_TO_FPING --version | sed 's/.*fping: Version //' | sed 's/\..*//')" + FPING_MINOR_VERSION="$($PATH_TO_FPING --version | sed 's/.*fping: Version //' | sed 's/.*\.//')" + + if test $FPING_MAJOR_VERSION -eq 5 ; then + if test $FPING_MINOR_VERSION -ge 3 ; then + AC_DEFINE(FPING_VERSION_5_3_OR_HIGHER, "true", [fping is of version 5.3 or higher]) + AC_MSG_NOTICE([fping is of version 5.3 or higher]) + AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher]) + AC_MSG_NOTICE([fping is of version 5.2 or higher]) + elif test $FPING_MINOR_VERSION -ge 2 ; then + AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher]) + AC_MSG_NOTICE([fping is of version 5.2 or higher]) + else + AC_MSG_NOTICE([fping is of a version lower then 5.2]) + fi + + elif $FPING_MAJOR_VERSION > 5 ; then + AC_DEFINE(FPING_VERSION_5_2_OR_HIGHER, "true", [fping is of version 5.2 or higher]) + AC_MSG_NOTICE([fping is of version 5.2 or higher]) + AC_DEFINE(FPING_VERSION_5_3_OR_HIGHER, "true", [fping is of version 5.2 or higher]) + AC_MSG_NOTICE([fping is of version 5.3 or higher]) + fi + + if test "`fping --version | sed 's/.*fping: Version //'`" = "5.2" ; then + AC_DEFINE(FPING_VERSION, "5.2", [the version of fping available]) + AC_MSG_NOTICE([fping version: 5.2]) + elif test "`fping --version | sed 's/.*fping: Version //'`" = "5.3"; then + AC_DEFINE(FPING_VERSION, "5.3", [the version of fping available]) + AC_MSG_NOTICE([fping version: 5.3]) + fi + fi else AC_MSG_WARN([Get fping from http://www.fping.com in order to make check_fping plugin]) fi -- cgit v1.2.3-74-g34f1 From 7247fc656a1f475159b7879cc3c3b798e03c1a33 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Thu, 12 Jun 2025 11:13:59 +0200 Subject: Implement new fping options for fping 5.2 and 5.3 fping 5.2 and 5.3 add some new useful command line options which this commit add to check_fping. These are: * --fwmark - sets a firewall mark in the packages to make them identifiable (fping 5.2) * --icmp-timestamp - fping uses ICMP timestamp instead of ICMP Echo (fping 5.2) * --check-source - fping discards replies which originate not from the target address (fping 5.2) The fping release notes describe theses options ( https://github.com/schweikert/fping/releases ) in a little bit more detail. Currently the help display for those options is only shown when fping was available in the appropriate version during compilation. --- plugins/check_fping.c | 80 +++++++++++++++++++++++++++++++++++++----- plugins/check_fping.d/config.h | 24 +++++++++++++ 2 files changed, 96 insertions(+), 8 deletions(-) diff --git a/plugins/check_fping.c b/plugins/check_fping.c index e05056b2..4d328a01 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -117,8 +117,26 @@ int main(int argc, char **argv) { xasprintf(&option_string, "%s-R ", option_string); } + if (config.fwmark_set) { + xasprintf(&option_string, "%s--fwmark %u ", option_string, config.fwmark); + } + + if (config.icmp_timestamp) { + xasprintf(&option_string, "%s--icmp-timestamp ", option_string); + } + + if (config.check_source) { + xasprintf(&option_string, "%s--check-source ", option_string); + } + char *command_line = NULL; - xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server); + + if (config.icmp_timestamp) { + // no paket size settable for ICMP timestamp + xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count, server); + } else { + xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server); + } if (verbose) { printf("%s\n", command_line); @@ -275,13 +293,35 @@ mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double c /* process command-line arguments */ check_fping_config_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = { - {"hostname", required_argument, 0, 'H'}, {"sourceip", required_argument, 0, 'S'}, {"sourceif", required_argument, 0, 'I'}, - {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, {"alive", no_argument, 0, 'a'}, - {"bytes", required_argument, 0, 'b'}, {"number", required_argument, 0, 'n'}, {"target-timeout", required_argument, 0, 'T'}, - {"interval", required_argument, 0, 'i'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, - {"dontfrag", no_argument, 0, 'M'}, {"random", no_argument, 0, 'R'}, {0, 0, 0, 0}}; + enum { + FWMARK_OPT = CHAR_MAX + 1, + ICMP_TIMESTAMP_OPT, + CHECK_SOURCE_OPT, + }; + static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, + {"sourceip", required_argument, 0, 'S'}, + {"sourceif", required_argument, 0, 'I'}, + {"critical", required_argument, 0, 'c'}, + {"warning", required_argument, 0, 'w'}, + {"alive", no_argument, 0, 'a'}, + {"bytes", required_argument, 0, 'b'}, + {"number", required_argument, 0, 'n'}, + {"target-timeout", required_argument, 0, 'T'}, + {"interval", required_argument, 0, 'i'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"dontfrag", no_argument, 0, 'M'}, + {"random", no_argument, 0, 'R'}, + // only available with fping version >= 5.3 + {"fwmark", required_argument, NULL, FWMARK_OPT}, + // only available with fping version >= 5.3 + {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT}, + {"check-source", no_argument, NULL, CHECK_SOURCE_OPT}, + + {0, 0, 0, 0}}; char *rv[2]; rv[PL] = NULL; @@ -409,6 +449,20 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) { case 'M': result.config.dontfrag = true; break; + case FWMARK_OPT: + if (is_intpos(optarg)) { + result.config.fwmark = (unsigned int)atol(optarg); + result.config.fwmark_set = true; + } else { + usage(_("fwmark must be a positive integer")); + } + break; + case ICMP_TIMESTAMP_OPT: + result.config.icmp_timestamp = true; + break; + case CHECK_SOURCE_OPT: + result.config.check_source = true; + break; } } @@ -496,6 +550,16 @@ void print_help(void) { printf(" %s\n", _("set the Don't Fragment flag")); printf(" %s\n", "-R, --random"); printf(" %s\n", _("random packet data (to foil link data compression)")); +#ifdef FPING_VERSION_5_2_OR_HIGHER + printf(" %s\n", "--fwmark=INTEGER"); + printf(" %s\n", _("set the routing mark to INTEGER (fping option)")); +# ifdef FPING_VERSION_5_3_OR_HIGHER + printf(" %s\n", "--icmp-timestamp"); + printf(" %s\n", _("use ICMP Timestamp instead of ICMP Echo (fping option)")); + printf(" %s\n", "--check-source"); + printf(" %s\n", _("discard replies not from target address (fping option)")); +# endif +#endif printf(UT_VERBOSE); printf("\n"); printf(" %s\n", _("THRESHOLD is ,%% where is the round trip average travel time (ms)")); diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h index a0697bf3..6d28382a 100644 --- a/plugins/check_fping.d/config.h +++ b/plugins/check_fping.d/config.h @@ -29,6 +29,21 @@ typedef struct { bool cpl_p; int wpl; bool wpl_p; + + // only available with fping version >= 5.2 + // for a given uint _fwmark_ fping sets _fwmark_ as a firewall mark + // in the pakets + unsigned int fwmark; + bool fwmark_set; + + + // only available with fping version >= 5.3 + // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead + // of ICMP Echo + bool icmp_timestamp; + + // Setting check_source lets fping discard replies which are not from the target address + bool check_source; } check_fping_config; check_fping_config check_fping_config_init() { @@ -53,6 +68,15 @@ check_fping_config check_fping_config_init() { .cpl_p = false, .wpl = 0, .wpl_p = false, + + // only available with fping version >= 5.2 + .fwmark = 0, + .fwmark_set = false, // just to be deterministic + + // only available with fping version >= 5.3 + .icmp_timestamp = false, + .check_source = false, + }; return tmp; } -- cgit v1.2.3-74-g34f1 From f2c6ce08e3da320d044564352faedd78eb76f1a1 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Thu, 12 Jun 2025 11:19:42 +0200 Subject: check_fping: small style improvement --- plugins/check_fping.c | 4 ++-- plugins/check_fping.d/config.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/check_fping.c b/plugins/check_fping.c index 4d328a01..c1e7dd17 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -132,7 +132,7 @@ int main(int argc, char **argv) { char *command_line = NULL; if (config.icmp_timestamp) { - // no paket size settable for ICMP timestamp + // no packet size settable for ICMP timestamp xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count, server); } else { xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server); @@ -346,7 +346,7 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) { argc--; } - while (1) { + while (true) { int option_index = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); if (option_index == -1 || option_index == EOF || option_index == 1) { diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h index 6d28382a..d95e9ded 100644 --- a/plugins/check_fping.d/config.h +++ b/plugins/check_fping.d/config.h @@ -32,7 +32,7 @@ typedef struct { // only available with fping version >= 5.2 // for a given uint _fwmark_ fping sets _fwmark_ as a firewall mark - // in the pakets + // in the packets unsigned int fwmark; bool fwmark_set; -- cgit v1.2.3-74-g34f1 From 19f409ac559278cd8623e2d5875818938f648996 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Thu, 12 Jun 2025 13:26:55 +0200 Subject: Remove unnecessary newline --- plugins/check_fping.c | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/check_fping.c b/plugins/check_fping.c index c1e7dd17..5db6b9aa 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -320,7 +320,6 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) { // only available with fping version >= 5.3 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT}, {"check-source", no_argument, NULL, CHECK_SOURCE_OPT}, - {0, 0, 0, 0}}; char *rv[2]; -- cgit v1.2.3-74-g34f1 From a669b2531d3b01aeb5b3e39c28bf2e6816fb14af Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Thu, 12 Jun 2025 13:33:50 +0200 Subject: Remove options if fping version is too low and die directly --- plugins/check_fping.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/check_fping.c b/plugins/check_fping.c index 5db6b9aa..8018e06d 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -315,11 +315,15 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) { {"use-ipv6", no_argument, 0, '6'}, {"dontfrag", no_argument, 0, 'M'}, {"random", no_argument, 0, 'R'}, - // only available with fping version >= 5.3 +#ifdef FPING_VERSION_5_2_OR_HIGHER + // only available with fping version >= 5.2 {"fwmark", required_argument, NULL, FWMARK_OPT}, +# ifdef FPING_VERSION_5_3_OR_HIGHER // only available with fping version >= 5.3 {"icmp-timestamp", no_argument, NULL, ICMP_TIMESTAMP_OPT}, {"check-source", no_argument, NULL, CHECK_SOURCE_OPT}, +# endif +#endif {0, 0, 0, 0}}; char *rv[2]; -- cgit v1.2.3-74-g34f1 From f680cd7b88aef2ff4ef8c4d336ee14269bea65bc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:19:30 +0200 Subject: Improve error detection for threshold parsers --- plugins-root/check_icmp.c | 98 +++++++++++++++++++++++++------------- plugins-root/check_icmp.d/config.h | 2 +- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index a57edef4..dad03ffa 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -138,7 +138,11 @@ void print_help(); void print_usage(void); /* Time related */ -static unsigned int get_timevar(const char *str); +typedef struct { + int error_code; + time_t time_range; +} get_timevar_wrapper; +static get_timevar_wrapper get_timevar(const char *str); static time_t get_timevaldiff(struct timeval earlier, struct timeval later); static time_t get_timevaldiff_to_now(struct timeval earlier); @@ -196,8 +200,8 @@ static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_s /* main test function */ static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval, uint16_t sender_id, check_icmp_execution_mode mode, - unsigned int max_completion_time, struct timeval prog_start, - ping_target **table, unsigned short packets, check_icmp_socket_set sockset, + time_t max_completion_time, struct timeval prog_start, ping_target **table, + unsigned short packets, check_icmp_socket_set sockset, unsigned short number_of_targets, check_icmp_state *program_state); mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, check_icmp_threshold warn, check_icmp_threshold crit); @@ -249,7 +253,7 @@ static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive mp_check overall[static 1]); /* Error exit */ -static void crash(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +static void crash(const char *fmt, ...) __attribute__((format(printf, 1, 2))); /** global variables **/ static int debug = 0; @@ -384,12 +388,24 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { MAX_PING_DATA - 1); } } break; - case 'i': - result.config.pkt_interval = get_timevar(optarg); - break; - case 'I': - result.config.target_interval = get_timevar(optarg); - break; + case 'i': { + get_timevar_wrapper parsed_time = get_timevar(optarg); + + if (parsed_time.error_code == OK) { + result.config.pkt_interval = parsed_time.time_range; + } else { + crash("failed to parse packet interval"); + } + } break; + case 'I': { + get_timevar_wrapper parsed_time = get_timevar(optarg); + + if (parsed_time.error_code == OK) { + result.config.target_interval = parsed_time.time_range; + } else { + crash("failed to parse target interval"); + } + } break; case 'w': { get_threshold_wrapper warn = get_threshold(optarg, result.config.warn); if (warn.errorcode == OK) { @@ -575,7 +591,7 @@ static void crash(const char *fmt, ...) { puts(""); exit(3); -} +} static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code) { const char *msg = "unreachable"; @@ -743,8 +759,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad /* source quench means we're sending too fast, so increase the * interval and mark this packet lost */ if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) { - *pkt_interval = (unsigned int)(*pkt_interval * PACKET_BACKOFF_FACTOR); - *target_interval = (unsigned int)(*target_interval * TARGET_BACKOFF_FACTOR); + *pkt_interval = (unsigned int)((double)*pkt_interval * PACKET_BACKOFF_FACTOR); + *target_interval = (unsigned int)((double)*target_interval * TARGET_BACKOFF_FACTOR); } else { program_state->targets_down++; host->flags |= FLAG_LOST_CAUSE; @@ -899,7 +915,7 @@ int main(int argc, char **argv) { gettimeofday(&prog_start, NULL); time_t max_completion_time = - ((config.pkt_interval *config.number_of_targets * config.number_of_packets) + + ((config.pkt_interval * config.number_of_targets * config.number_of_packets) + (config.target_interval * config.number_of_targets)) + (config.number_of_targets * config.number_of_packets * config.crit.rta) + config.crit.rta; @@ -921,7 +937,7 @@ int main(int argc, char **argv) { } if (debug) { - printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", config.crit.rta, config.crit.pl, + printf("crit = {%ld, %u%%}, warn = {%ld, %u%%}\n", config.crit.rta, config.crit.pl, config.warn.rta, config.warn.pl); printf("pkt_interval: %ld target_interval: %ld\n", config.pkt_interval, config.target_interval); @@ -976,7 +992,7 @@ int main(int argc, char **argv) { static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval, const uint16_t sender_id, const check_icmp_execution_mode mode, - const unsigned int max_completion_time, const struct timeval prog_start, + const time_t max_completion_time, const struct timeval prog_start, ping_target **table, const unsigned short packets, const check_icmp_socket_set sockset, const unsigned short number_of_targets, check_icmp_state *program_state) { @@ -1028,7 +1044,7 @@ static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_ time_t final_wait = max_completion_time - time_passed; if (debug) { - printf("time_passed: %ld final_wait: %ld max_completion_time: %u\n", time_passed, + printf("time_passed: %ld final_wait: %ld max_completion_time: %ld\n", time_passed, final_wait, max_completion_time); } if (time_passed > max_completion_time) { @@ -1138,7 +1154,6 @@ static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_inter parse_address(&resp_addr, address, sizeof(address)); crash("received packet too short for ICMP (%ld bytes, expected %d) from %s\n", recv_foo.received, hlen + icmp_pkt_size, address); - } /* check the response */ memcpy(packet.buf, buf + hlen, icmp_pkt_size); @@ -1272,7 +1287,7 @@ static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host data.ping_id = 10; /* host->icmp.icmp_sent; */ memcpy(&data.stime, ¤t_time, sizeof(current_time)); - socklen_t addrlen; + socklen_t addrlen = 0; if (host->address.ss_family == AF_INET) { struct icmp *icp = (struct icmp *)buf; @@ -1789,14 +1804,21 @@ static in_addr_t get_ip_address(const char *ifname) { * s = seconds * return value is in microseconds */ -static unsigned int get_timevar(const char *str) { +static get_timevar_wrapper get_timevar(const char *str) { + get_timevar_wrapper result = { + .error_code = OK, + .time_range = 0, + }; + if (!str) { - return 0; + result.error_code = ERROR; + return result; } size_t len = strlen(str); if (!len) { - return 0; + result.error_code = ERROR; + return result; } /* unit might be given as ms|m (millisec), @@ -1834,12 +1856,14 @@ static unsigned int get_timevar(const char *str) { unsigned long pre_radix; pre_radix = strtoul(str, &ptr, 0); if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { - return (unsigned int)(pre_radix * factor); + result.time_range = (unsigned int)(pre_radix * factor); + return result; } /* time specified in usecs can't have decimal points, so ignore them */ if (factor == 1) { - return (unsigned int)pre_radix; + result.time_range = (unsigned int)pre_radix; + return result; } /* integer and decimal, respectively */ @@ -1851,10 +1875,10 @@ static unsigned int get_timevar(const char *str) { } /* the last parenthesis avoids floating point exceptions. */ - return (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10))); + result.time_range = (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10))); + return result; } -/* not too good at checking errors, but it'll do (main() should barfe on -1) */ static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) { get_threshold_wrapper result = { .errorcode = OK, @@ -1880,9 +1904,15 @@ static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold thres is_at_last_char = true; tmp--; } - result.threshold.rta = get_timevar(str); - if (!result.threshold.rta) { + get_timevar_wrapper parsed_time = get_timevar(str); + + if (parsed_time.error_code == OK) { + result.threshold.rta = parsed_time.time_range; + } else { + if (debug > 1) { + printf("%s: failed to parse rta threshold\n", __FUNCTION__); + } result.errorcode = ERROR; return result; } @@ -2170,7 +2200,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, xasprintf(&result.output, "%s", address); double packet_loss; - double rta; + time_t rta; if (!target.icmp_recv) { /* rta 0 is of course not entirely correct, but will still show up * conspicuously as missing entries in perfparse and cacti */ @@ -2188,7 +2218,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, } else { packet_loss = (unsigned char)((target.icmp_sent - target.icmp_recv) * 100) / target.icmp_sent; - rta = (double)target.time_waited / target.icmp_recv; + rta = target.time_waited / target.icmp_recv; } double EffectiveLatency; @@ -2219,7 +2249,7 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, * round trip jitter, but double the impact to latency * then add 10 for protocol latencies (in milliseconds). */ - EffectiveLatency = (rta / 1000) + target.jitter * 2 + 10; + EffectiveLatency = ((double)rta / 1000) + target.jitter * 2 + 10; double R; if (EffectiveLatency < 160) { @@ -2249,14 +2279,14 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (modes.rta_mode) { mp_subcheck sc_rta = mp_subcheck_init(); sc_rta = mp_set_subcheck_default_state(sc_rta, STATE_OK); - xasprintf(&sc_rta.output, "rta %0.3fms", rta / 1000); + xasprintf(&sc_rta.output, "rta %0.3fms", (double)rta / 1000); if (rta >= crit.rta) { sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL); - xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double) crit.rta / 1000); + xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double)crit.rta / 1000); } else if (rta >= warn.rta) { sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING); - xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double) warn.rta / 1000); + xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double)warn.rta / 1000); } if (packet_loss < 100) { diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index 97be7fc1..fc9dd5a6 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -16,7 +16,7 @@ /* threshold structure. all values are maximum allowed, exclusive */ typedef struct { unsigned char pl; /* max allowed packet loss in percent */ - unsigned int rta; /* roundtrip time average, microseconds */ + time_t rta; /* roundtrip time average, microseconds */ double jitter; /* jitter time average, microseconds */ double mos; /* MOS */ double score; /* Score */ -- cgit v1.2.3-74-g34f1 From 43ae5fbe2df66c6c3169528431b6b0c9c6bfd67c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:44:35 +0200 Subject: Fix possible multiplication overflow? --- plugins-root/check_icmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index dad03ffa..7ee08ad9 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -917,7 +917,7 @@ int main(int argc, char **argv) { time_t max_completion_time = ((config.pkt_interval * config.number_of_targets * config.number_of_packets) + (config.target_interval * config.number_of_targets)) + - (config.number_of_targets * config.number_of_packets * config.crit.rta) + config.crit.rta; + (config.crit.rta * config.number_of_targets * config.number_of_packets) + config.crit.rta; if (debug) { printf("packets: %u, targets: %u\n" -- cgit v1.2.3-74-g34f1 From ca954e4f765ea336e4aef633b38c31a7c6a99d75 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 19 Jun 2025 00:55:41 +0200 Subject: Reintroduce min_hosts_alive --- plugins-root/check_icmp.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 7ee08ad9..b97a68ed 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -1515,6 +1515,9 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, targets_alive(number_of_targets, program_state->targets_down)); } + mp_subcheck sc_single_targets = mp_subcheck_init(); + xasprintf(&sc_single_targets.output, "Individual Hosts"); + // loop over targets to evaluate each one int targets_ok = 0; int targets_warn = 0; @@ -1524,7 +1527,15 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, targets_ok += host_check.targets_ok; targets_warn += host_check.targets_warn; - mp_add_subcheck_to_check(overall, host_check.sc_host); + if (min_hosts_alive > -1) { + mp_add_subcheck_to_subcheck(&sc_single_targets, host_check.sc_host); + } else { + mp_add_subcheck_to_check(overall, host_check.sc_host); + } + } + + if (min_hosts_alive > -1) { + mp_add_subcheck_to_check(overall, sc_single_targets); } if (number_of_hosts == 1) { @@ -1545,11 +1556,20 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK); if (targets_ok >= min_hosts_alive) { - // TODO this should overwrite the main state sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK); + xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", + targets_ok, min_hosts_alive); } else if ((targets_ok + targets_warn) >= min_hosts_alive) { sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING); + xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u", + targets_ok + targets_warn, min_hosts_alive); + } else { + sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_CRITICAL); + xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u", + targets_ok + targets_warn, min_hosts_alive); } + + mp_add_subcheck_to_check(overall, sc_min_targets_alive); } /* finish with an empty line */ -- cgit v1.2.3-74-g34f1 From b71cb430cb79e89b5d8bf56990919b6c753cd271 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 19 Jun 2025 01:15:11 +0200 Subject: Implement flexible state override functions --- lib/output.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++------------- lib/output.h | 31 ++++++++++++++++++----- 2 files changed, 90 insertions(+), 24 deletions(-) diff --git a/lib/output.c b/lib/output.c index c408a2f5..b398c2ad 100644 --- a/lib/output.c +++ b/lib/output.c @@ -16,7 +16,8 @@ static mp_output_format output_format = MP_FORMAT_DEFAULT; static mp_output_detail_level level_of_detail = MP_DETAIL_ALL; // == Prototypes == -static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation); +static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, + unsigned int indentation); static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck); // == Implementation == @@ -58,7 +59,9 @@ static inline char *fmt_subcheck_perfdata(mp_subcheck check) { * It sets useful defaults */ mp_check mp_check_init(void) { - mp_check check = {0}; + mp_check check = { + .evaluation_function = &mp_eval_check_default, + }; return check; } @@ -121,7 +124,8 @@ void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata */ int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) { if (subcheck.output == NULL) { - die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL"); + die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, + "Sub check output is NULL"); } mp_subcheck_list *tmp = NULL; @@ -194,18 +198,30 @@ char *get_subcheck_summary(mp_check check) { return result; } +mp_state_enum mp_compute_subcheck_state(const mp_subcheck subcheck) { + if (subcheck.evaluation_function == NULL) { + return mp_eval_subcheck_default(subcheck); + } + return subcheck.evaluation_function(subcheck); +} + /* - * Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states + * Generate the result state of a mp_subcheck object based on its own state and its subchecks + * states */ -mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) { - if (check.state_set_explicitly) { - return check.state; +mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck) { + if (subcheck.evaluation_function != NULL) { + return subcheck.evaluation_function(subcheck); } - mp_subcheck_list *scl = check.subchecks; + if (subcheck.state_set_explicitly) { + return subcheck.state; + } + + mp_subcheck_list *scl = subcheck.subchecks; if (scl == NULL) { - return check.default_state; + return subcheck.default_state; } mp_state_enum result = STATE_OK; @@ -218,10 +234,18 @@ mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) { return result; } +mp_state_enum mp_compute_check_state(const mp_check check) { + // just a safety check + if (check.evaluation_function == NULL) { + return mp_eval_check_default(check); + } + return check.evaluation_function(check); +} + /* * Generate the result state of a mp_check object based on it's own state and it's subchecks states */ -mp_state_enum mp_compute_check_state(const mp_check check) { +mp_state_enum mp_eval_check_default(const mp_check check) { assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here mp_subcheck_list *scl = check.subchecks; @@ -253,8 +277,10 @@ char *mp_fmt_output(mp_check check) { mp_subcheck_list *subchecks = check.subchecks; while (subchecks != NULL) { - if (level_of_detail == MP_DETAIL_ALL || mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) { - asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); + if (level_of_detail == MP_DETAIL_ALL || + mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) { + asprintf(&result, "%s\n%s", result, + fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); } subchecks = subchecks->next; } @@ -266,7 +292,8 @@ char *mp_fmt_output(mp_check check) { if (pd_string == NULL) { asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); } else { - asprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck)); + asprintf(&pd_string, "%s %s", pd_string, + fmt_subcheck_perfdata(subchecks->subcheck)); } subchecks = subchecks->next; @@ -335,19 +362,21 @@ static char *generate_indentation_string(unsigned int indentation) { /* * Helper function to generate the output string of mp_subcheck */ -static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) { +static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, + unsigned int indentation) { char *result = NULL; mp_subcheck_list *subchecks = NULL; switch (output_format) { case MP_FORMAT_MULTI_LINE: - asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), - check.output); + asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), + state_text(mp_compute_subcheck_state(check)), check.output); subchecks = check.subchecks; while (subchecks != NULL) { - asprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); + asprintf(&result, "%s\n%s", result, + fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); subchecks = subchecks->next; } return result; @@ -551,3 +580,23 @@ mp_output_format mp_get_format(void) { return output_format; } void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; } mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; } + +mp_state_enum mp_eval_ok(mp_check overall) { + (void)overall; + return STATE_OK; +} + +mp_state_enum mp_eval_warning(mp_check overall) { + (void)overall; + return STATE_WARNING; +} + +mp_state_enum mp_eval_critical(mp_check overall) { + (void)overall; + return STATE_CRITICAL; +} + +mp_state_enum mp_eval_unknown(mp_check overall) { + (void)overall; + return STATE_UNKNOWN; +} diff --git a/lib/output.h b/lib/output.h index 3bd91f90..c63c8e3f 100644 --- a/lib/output.h +++ b/lib/output.h @@ -7,15 +7,21 @@ /* * A partial check result */ -typedef struct { +typedef struct mp_subcheck mp_subcheck; +struct mp_subcheck { mp_state_enum state; // OK, Warning, Critical ... set explicitly mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly - bool state_set_explicitly; // was the state set explicitly (or should it be derived from subchecks) + bool state_set_explicitly; // was the state set explicitly (or should it be derived from + // subchecks) - char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP connection to..") - pd_list *perfdata; // Performance data for this check + char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP + // connection to..") + pd_list *perfdata; // Performance data for this check struct subcheck_list *subchecks; // subchecks deeper in the hierarchy -} mp_subcheck; + + // the evaluation_functions computes the state of subcheck + mp_state_enum (*evaluation_function)(mp_subcheck); +}; /* * A list of subchecks, used in subchecks and the main check @@ -57,10 +63,14 @@ mp_output_detail_level mp_get_level_of_detail(void); * The final result is always derived from the children and the "worst" state * in the first layer of subchecks */ -typedef struct { +typedef struct mp_check mp_check; +struct mp_check { char *summary; // Overall summary, if not set a summary will be automatically generated mp_subcheck_list *subchecks; -} mp_check; + + // the evaluation_functions computes the state of check + mp_state_enum (*evaluation_function)(mp_check); +}; mp_check mp_check_init(void); mp_subcheck mp_subcheck_init(void); @@ -78,6 +88,13 @@ void mp_add_summary(mp_check check[static 1], char *summary); mp_state_enum mp_compute_check_state(mp_check); mp_state_enum mp_compute_subcheck_state(mp_subcheck); +mp_state_enum mp_eval_ok(mp_check overall); +mp_state_enum mp_eval_warning(mp_check overall); +mp_state_enum mp_eval_critical(mp_check overall); +mp_state_enum mp_eval_unknown(mp_check overall); +mp_state_enum mp_eval_check_default(mp_check check); +mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck); + typedef struct { bool parsing_success; mp_output_format output_format; -- cgit v1.2.3-74-g34f1 From 20e938bb542d361ad1468ab24330f554445aefb1 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 19 Jun 2025 01:15:37 +0200 Subject: Override state with min_hosts_alive --- plugins-root/check_icmp.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index b97a68ed..7360b435 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -1515,9 +1515,6 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, targets_alive(number_of_targets, program_state->targets_down)); } - mp_subcheck sc_single_targets = mp_subcheck_init(); - xasprintf(&sc_single_targets.output, "Individual Hosts"); - // loop over targets to evaluate each one int targets_ok = 0; int targets_warn = 0; @@ -1527,29 +1524,16 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, targets_ok += host_check.targets_ok; targets_warn += host_check.targets_warn; - if (min_hosts_alive > -1) { - mp_add_subcheck_to_subcheck(&sc_single_targets, host_check.sc_host); - } else { - mp_add_subcheck_to_check(overall, host_check.sc_host); - } - } - - if (min_hosts_alive > -1) { - mp_add_subcheck_to_check(overall, sc_single_targets); - } - - if (number_of_hosts == 1) { - // Exit early here, since the other checks only make sense for multiple hosts - return; + mp_add_subcheck_to_check(overall, host_check.sc_host); } /* this is inevitable */ - if (targets_alive(number_of_targets, program_state->targets_down) == 0) { - mp_subcheck sc_no_target_alive = mp_subcheck_init(); - sc_no_target_alive = mp_set_subcheck_state(sc_no_target_alive, STATE_CRITICAL); - sc_no_target_alive.output = strdup("No target is alive!"); - mp_add_subcheck_to_check(overall, sc_no_target_alive); - } + // if (targets_alive(number_of_targets, program_state->targets_down) == 0) { + // mp_subcheck sc_no_target_alive = mp_subcheck_init(); + // sc_no_target_alive = mp_set_subcheck_state(sc_no_target_alive, STATE_CRITICAL); + // sc_no_target_alive.output = strdup("No target is alive!"); + // mp_add_subcheck_to_check(overall, sc_no_target_alive); + // } if (min_hosts_alive > -1) { mp_subcheck sc_min_targets_alive = mp_subcheck_init(); @@ -1557,16 +1541,21 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, if (targets_ok >= min_hosts_alive) { sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK); - xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", - targets_ok, min_hosts_alive); + xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", targets_ok, + min_hosts_alive); + + // Overwrite main state here + overall->evaluation_function = &mp_eval_ok; } else if ((targets_ok + targets_warn) >= min_hosts_alive) { sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING); xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u", targets_ok + targets_warn, min_hosts_alive); + overall->evaluation_function = &mp_eval_warning; } else { sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_CRITICAL); xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u", targets_ok + targets_warn, min_hosts_alive); + overall->evaluation_function = &mp_eval_critical; } mp_add_subcheck_to_check(overall, sc_min_targets_alive); -- cgit v1.2.3-74-g34f1 From 2bea8e9522af1c454d1a6a618402f9f342730406 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 20 Jun 2025 10:04:34 +0200 Subject: check_icmp: Remove signal handling and timeouts The timeout option was redundant in that the runtime of check_icmp was always limited by the input parameters and therefore timeout gets removed with this commit to avoid that confusion. The rest of the signal handlings was removed too, since the added complexity does not provide sufficient returns. If check_icmp gets a signal, it now dies like most other programs instead of trying to save some things and return a (arguably wrong) result. --- plugins-root/check_icmp.c | 18 +--------------- plugins-root/check_icmp.d/check_icmp_helpers.c | 19 ---------------- plugins-root/check_icmp.d/check_icmp_helpers.h | 2 -- plugins-root/t/check_icmp.t | 30 +++++++++++++------------- 4 files changed, 16 insertions(+), 53 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 7360b435..55405b8a 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -431,8 +431,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { } break; case 't': - timeout = (unsigned int)strtoul(optarg, NULL, 0); - // TODO die here and complain about wrong input + // WARNING Deprecated since execution time is determined by the other factors break; case 'H': { add_host_wrapper host_add_result = @@ -895,21 +894,6 @@ int main(int argc, char **argv) { } } - struct sigaction sig_action; - sig_action.sa_handler = NULL; - sig_action.sa_sigaction = check_icmp_timeout_handler; - sigfillset(&sig_action.sa_mask); - sig_action.sa_flags = SA_NODEFER | SA_RESTART | SA_SIGINFO; - - sigaction(SIGINT, &sig_action, NULL); - sigaction(SIGHUP, &sig_action, NULL); - sigaction(SIGTERM, &sig_action, NULL); - sigaction(SIGALRM, &sig_action, NULL); - if (debug) { - printf("Setting alarm timeout to %u seconds\n", timeout); - } - alarm(timeout); - /* make sure we don't wait any longer than necessary */ struct timeval prog_start; gettimeofday(&prog_start, NULL); diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 7a936cc9..ec786305 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -132,22 +132,3 @@ unsigned int ping_target_list_append(ping_target *list, ping_target *elem) { return result; } - -void check_icmp_timeout_handler(int signal, siginfo_t *info, void *ucontext) { - // Ignore unused arguments - (void)info; - (void)ucontext; - mp_subcheck timeout_sc = mp_subcheck_init(); - timeout_sc = mp_set_subcheck_state(timeout_sc, socket_timeout_state); - - if (signal == SIGALRM) { - xasprintf(&timeout_sc.output, _("timeout after %d seconds\n"), timeout); - } else { - xasprintf(&timeout_sc.output, _("timeout after %d seconds\n"), timeout); - } - - mp_check overall = mp_check_init(); - mp_add_subcheck_to_check(&overall, timeout_sc); - - mp_exit(overall); -} diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h index 1b9372ce..dc6ea40b 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.h +++ b/plugins-root/check_icmp.d/check_icmp_helpers.h @@ -66,5 +66,3 @@ typedef struct { ping_target_create_wrapper ping_target_create(struct sockaddr_storage address); unsigned int ping_target_list_append(ping_target *list, ping_target *elem); - -void check_icmp_timeout_handler(int, siginfo_t *, void *); diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t index 2c1d12e6..e68617cd 100644 --- a/plugins-root/t/check_icmp.t +++ b/plugins-root/t/check_icmp.t @@ -33,12 +33,12 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID", my $res; $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -w 10000ms,100% -c 10000ms,100%" + "$sudo ./check_icmp -H $host_responsive -w 100ms,100% -c 100ms,100%" ); is( $res->return_code, 0, "Syntax ok" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 10000ms,100%" + "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 100ms,100%" ); is( $res->return_code, 1, "Syntax ok, with forced warning" ); @@ -48,32 +48,32 @@ $res = NPTest->testCmd( is( $res->return_code, 2, "Syntax ok, with forced critical" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -t 2" + "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100%" ); is( $res->return_code, 2, "Timeout - host nonresponsive" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -w 10000ms,100% -c 10000ms,100%" + "$sudo ./check_icmp -w 100ms,100% -c 100ms,100%" ); is( $res->return_code, 3, "No hostname" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 0 -t 2" + "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 0" ); is( $res->return_code, 0, "One host nonresponsive - zero required" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 1 -t 2" + "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 1" ); is( $res->return_code, 0, "One of two host nonresponsive - one required" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" + "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 2" ); is( $res->return_code, 2, "One of two host nonresponsive - two required" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" + "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 100ms,100% -c 100ms,100% -n 1 -m 2" ); is( $res->return_code, 0, "IPv4 source_ip accepted" ); @@ -83,35 +83,35 @@ $res = NPTest->testCmd( is( $res->return_code, 0, "Try max packet size" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1 -t 2" + "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1" ); is( $res->return_code, 0, "rta works" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1 -t 2" + "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1" ); is( $res->return_code, 0, "pl works" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -J 80,90 -t 2" + "$sudo ./check_icmp -H $host_responsive -J 80,90" ); is( $res->return_code, 0, "jitter works" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -M 4,3 -t 2" + "$sudo ./check_icmp -H $host_responsive -M 4,3" ); is( $res->return_code, 0, "mos works" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -S 80,70 -t 2" + "$sudo ./check_icmp -H $host_responsive -S 80,70" ); is( $res->return_code, 0, "score works" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -O -t 2" + "$sudo ./check_icmp -H $host_responsive -O" ); is( $res->return_code, 0, "order works" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100 -t 2" + "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100" ); is( $res->return_code, 0, "order works" ); -- cgit v1.2.3-74-g34f1 From 6bc2e75199519d0a7677dff09137558ef6eb761c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 20 Jun 2025 10:28:45 +0200 Subject: Fix test with weird arguments --- plugins-root/t/check_icmp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t index e68617cd..cbf3dda5 100644 --- a/plugins-root/t/check_icmp.t +++ b/plugins-root/t/check_icmp.t @@ -73,7 +73,7 @@ $res = NPTest->testCmd( is( $res->return_code, 2, "One of two host nonresponsive - two required" ); $res = NPTest->testCmd( - "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 100ms,100% -c 100ms,100% -n 1 -m 2" + "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 100ms,100% -c 100ms,100% -n 1" ); is( $res->return_code, 0, "IPv4 source_ip accepted" ); -- cgit v1.2.3-74-g34f1 From d2735eecd4c44ba4a2504304963e861a427e393e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 20 Jun 2025 10:53:46 +0200 Subject: Fix number of tests --- plugins-root/t/check_icmp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t index cbf3dda5..d414c3c7 100644 --- a/plugins-root/t/check_icmp.t +++ b/plugins-root/t/check_icmp.t @@ -12,7 +12,7 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO", "no" ); if ($allow_sudo eq "yes" or $> == 0) { - plan tests => 40; + plan tests => 17; } else { plan skip_all => "Need sudo to test check_icmp"; } -- cgit v1.2.3-74-g34f1 From 8ae415ee4ceddeed1c1a1e0e6e64175cff6731c3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 09:26:13 +0200 Subject: Improve check_icmp help output --- plugins-root/check_icmp.c | 48 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 55405b8a..0c69d31c 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -2045,32 +2045,32 @@ unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) { } void print_help(void) { - /*print_revision (progname);*/ /* FIXME: Why? */ + // print_revision (progname); /* FIXME: Why? */ printf("Copyright (c) 2005 Andreas Ericsson \n"); printf(COPYRIGHT, copyright, email); - printf("\n\n"); - print_usage(); printf(UT_HELP_VRSN); printf(UT_EXTRA_OPTS); printf(" %s\n", "-H"); - printf(" %s\n", _("specify a target")); + printf(" %s\n", + _("specify a target, might be one of: resolveable name, IPv6 address, IPv4 address " + "(necessary, can be given multiple times)")); printf(" %s\n", "[-4|-6]"); - printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); + printf(" %s\n", _("Use IPv4 or IPv6 only to communicate with the targets")); printf(" %s\n", "-w"); - printf(" %s", _("warning threshold (currently ")); + printf(" %s", _("warning threshold (default ")); printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL); printf(" %s\n", "-c"); - printf(" %s", _("critical threshold (currently ")); + printf(" %s", _("critical threshold (default ")); printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL); printf(" %s\n", "-R"); printf(" %s\n", - _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); + _("RTA (round trip average) mode warning,critical, ex. 100ms,200ms unit in ms")); printf(" %s\n", "-P"); printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); printf(" %s\n", "-J"); @@ -2080,42 +2080,34 @@ void print_help(void) { printf(" %s\n", "-S"); printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 ")); printf(" %s\n", "-O"); - printf(" %s\n", _("detect out of order ICMP packts ")); - printf(" %s\n", "-H"); - printf(" %s\n", _("specify a target")); - printf(" %s\n", "-s"); - printf(" %s\n", _("specify a source IP address or device name")); + printf(" %s\n", + _("detect out of order ICMP packts, if such packets are found, the result is CRITICAL")); printf(" %s\n", "-n"); - printf(" %s", _("number of packets to send (currently ")); - printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS); printf(" %s\n", "-p"); - printf(" %s", _("number of packets to send (currently ")); + printf(" %s", _("number of packets to send (default ")); printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS); printf(" %s\n", "-i"); - printf(" %s", _("max packet interval (currently ")); + printf(" %s", _("max packet interval (default ")); printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000); printf(" %s\n", "-I"); - printf(" %s", _("max target interval (currently ")); - printf("%0.3fms)\n", (float)DEFAULT_TARGET_INTERVAL / 1000); + printf(" %s%0.3fms)\n The time intervall to wait in between one target and the next", + _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000); printf(" %s\n", "-m"); printf(" %s", _("number of alive hosts required for success")); printf("\n"); printf(" %s\n", "-l"); - printf(" %s", _("TTL on outgoing packets (currently ")); + printf(" %s", _("TTL on outgoing packets (default ")); printf("%u)\n", DEFAULT_TTL); - printf(" %s\n", "-t"); - printf(" %s", _("timeout value (seconds, currently ")); - printf("%u)\n", DEFAULT_TIMEOUT); printf(" %s\n", "-b"); printf(" %s\n", _("Number of icmp data bytes to send")); - printf(" %s %lu + %d)\n", _("Packet size will be data bytes + icmp header (currently"), + printf(" %s %lu + %d)\n", _("Packet size will be data bytes + icmp header (default"), DEFAULT_PING_DATA_SIZE, ICMP_MINLEN); printf(" %s\n", "-v"); - printf(" %s\n", _("verbose")); + printf(" %s\n", _("Verbosity, can be given multiple times (for debugging)")); printf("\n"); printf("%s\n", _("Notes:")); printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P")); - printf(" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not.")); + printf(" %s\n", _("Naming a host (or several) to check is not.")); printf("\n"); printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); printf(" %s\n", _("packet loss. The default values should work well for most users.")); @@ -2128,8 +2120,6 @@ void print_help(void) { hops")); printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent.")); printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/ - printf("\n"); - printf(" %s\n", _("The -v switch can be specified several times for increased verbosity.")); /* printf ("%s\n", _("Long options are currently unsupported.")); printf ("%s\n", _("Options marked with * require an argument")); */ @@ -2139,7 +2129,7 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf(" %s [options] [-H] host1 host2 hostN\n", progname); + printf(" %s [options] [-H host1 [-H host2 [-H hostN]]]\n", progname); } static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode, -- cgit v1.2.3-74-g34f1 From cd20cc063245523d51013849fa28eb0cac013171 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:17:28 +0200 Subject: check_icmp: add long options, add output format option This commit switches check_icmp from getopt to getopt_long to provide long options too and (most importantly) homogenize option parsing between the different plugins. --- plugins-root/check_icmp.c | 52 +++++++++++++++++++++++++- plugins-root/check_icmp.d/check_icmp_helpers.c | 2 + plugins-root/check_icmp.d/config.h | 6 ++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 0c69d31c..bad73cc4 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -319,12 +319,41 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { sa_family_t enforced_ai_family = AF_UNSPEC; + enum { + output_format_index = CHAR_MAX + 1, + }; + + struct option longopts[] = { + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"Host", required_argument, 0, 'H'}, + {"ipv4-only", no_argument, 0, '4'}, + {"ipv6-only", no_argument, 0, '6'}, + {"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, + {"rta-mode-thresholds", required_argument, 0, 'R'}, + {"packet-loss-mode-thresholds", required_argument, 0, 'P'}, + {"jitter-mode-thresholds", required_argument, 0, 'J'}, + {"mos-mode-thresholds", required_argument, 0, 'M'}, + {"score-mode-thresholds", required_argument, 0, 'S'}, + {"out-of-order-packets", no_argument, 0, 'O'}, + {"number-of-packets", required_argument, 0, 'n'}, + {"number-of-packets", required_argument, 0, 'p'}, + {"packet-interval", required_argument, 0, 'i'}, + {"target-interval", required_argument, 0, 'I'}, + {"outgoing-ttl", required_argument, 0, 'l'}, + {"packet_payload_size", required_argument, 0, 'b'}, + {"output-format", required_argument, 0, output_format_index}, + {}, + }; + // Parse protocol arguments first // and count hosts here char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64"; for (int i = 1; i < argc; i++) { long int arg; - while ((arg = getopt(argc, argv, opts_str)) != EOF) { + while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) { switch (arg) { case '4': if (enforced_ai_family != AF_UNSPEC) { @@ -374,7 +403,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { /* parse the arguments */ for (int i = 1; i < argc; i++) { long int arg; - while ((arg = getopt(argc, argv, opts_str)) != EOF) { + while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) { switch (arg) { case 'b': { long size = strtol(optarg, NULL, 0); @@ -536,6 +565,18 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { case 'O': /* out of order mode */ result.config.modes.order_mode = true; break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } } } } @@ -803,6 +844,10 @@ int main(int argc, char **argv) { const check_icmp_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + // int icmp_proto = IPPROTO_ICMP; // add_target might change address_family // switch (address_family) { @@ -2104,6 +2149,9 @@ void print_help(void) { DEFAULT_PING_DATA_SIZE, ICMP_MINLEN); printf(" %s\n", "-v"); printf(" %s\n", _("Verbosity, can be given multiple times (for debugging)")); + + printf(UT_OUTPUT_FORMAT); + printf("\n"); printf("%s\n", _("Notes:")); printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P")); diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index ec786305..9acc96fd 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -52,6 +52,8 @@ check_icmp_config check_icmp_config_init() { .number_of_hosts = 0, .hosts = NULL, + + .output_format_is_set = false, }; return tmp; } diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index fc9dd5a6..8092e343 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -12,11 +12,12 @@ #include #include #include "./check_icmp_helpers.h" +#include "output.h" /* threshold structure. all values are maximum allowed, exclusive */ typedef struct { unsigned char pl; /* max allowed packet loss in percent */ - time_t rta; /* roundtrip time average, microseconds */ + time_t rta; /* roundtrip time average, microseconds */ double jitter; /* jitter time average, microseconds */ double mos; /* MOS */ double score; /* Score */ @@ -77,6 +78,9 @@ typedef struct { unsigned short number_of_hosts; check_icmp_target_container *hosts; + + mp_output_format output_format; + bool output_format_is_set; } check_icmp_config; check_icmp_config check_icmp_config_init(); -- cgit v1.2.3-74-g34f1 From efba4f76545fe03aa9b9af35f23e6f7aad5aabcd Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:32:24 +0200 Subject: check_icmp: cleanup some leftover comments --- plugins-root/check_icmp.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index bad73cc4..7f01e1b6 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -484,7 +484,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.need_v6 = true; } } else { - // TODO adding host failed, crash here + crash("Failed to add host, unable to parse it correctly"); } } break; case 'l': @@ -848,19 +848,6 @@ int main(int argc, char **argv) { mp_set_format(config.output_format); } - // int icmp_proto = IPPROTO_ICMP; - // add_target might change address_family - // switch (address_family) { - // case AF_INET: - // icmp_proto = IPPROTO_ICMP; - // break; - // case AF_INET6: - // icmp_proto = IPPROTO_ICMPV6; - // break; - // default: - // crash("Address family not supported"); - // } - check_icmp_socket_set sockset = { .socket4 = -1, .socket6 = -1, @@ -1556,14 +1543,6 @@ static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive, mp_add_subcheck_to_check(overall, host_check.sc_host); } - /* this is inevitable */ - // if (targets_alive(number_of_targets, program_state->targets_down) == 0) { - // mp_subcheck sc_no_target_alive = mp_subcheck_init(); - // sc_no_target_alive = mp_set_subcheck_state(sc_no_target_alive, STATE_CRITICAL); - // sc_no_target_alive.output = strdup("No target is alive!"); - // mp_add_subcheck_to_check(overall, sc_no_target_alive); - // } - if (min_hosts_alive > -1) { mp_subcheck sc_min_targets_alive = mp_subcheck_init(); sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK); -- cgit v1.2.3-74-g34f1 From 27d30c3df0904ef4c81ad5adc50fa451777693a9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:34:12 +0200 Subject: Fix typo --- plugins-root/check_icmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 7f01e1b6..d181af56 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -2114,7 +2114,7 @@ void print_help(void) { printf(" %s", _("max packet interval (default ")); printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000); printf(" %s\n", "-I"); - printf(" %s%0.3fms)\n The time intervall to wait in between one target and the next", + printf(" %s%0.3fms)\n The time interval to wait in between one target and the next", _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000); printf(" %s\n", "-m"); printf(" %s", _("number of alive hosts required for success")); -- cgit v1.2.3-74-g34f1 From b08bafc45cea008abb46e0891c55e7745e117aae Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:52:56 +0200 Subject: check_icmp: remove paket_interval, was never used anyway --- plugins-root/check_icmp.c | 88 +++++++++++--------------- plugins-root/check_icmp.d/check_icmp_helpers.c | 1 - plugins-root/check_icmp.d/config.h | 1 - 3 files changed, 37 insertions(+), 53 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index d181af56..524e1fb9 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -151,10 +151,9 @@ static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family); /* Receiving data */ static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval, - unsigned short icmp_pkt_size, time_t *pkt_interval, - time_t *target_interval, uint16_t sender_id, ping_target **table, - unsigned short packets, unsigned short number_of_targets, - check_icmp_state *program_state); + unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id, + ping_target **table, unsigned short packets, + unsigned short number_of_targets, check_icmp_state *program_state); typedef struct { sa_family_t recv_proto; @@ -164,9 +163,9 @@ static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *bu struct sockaddr *saddr, time_t *timeout, struct timeval *received_timestamp); static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, - time_t *pkt_interval, time_t *target_interval, uint16_t sender_id, - ping_target **table, unsigned short packets, - unsigned short number_of_targets, check_icmp_state *program_state); + time_t *target_interval, uint16_t sender_id, ping_target **table, + unsigned short packets, unsigned short number_of_targets, + check_icmp_state *program_state); /* Sending data */ static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host, @@ -198,11 +197,11 @@ static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_s threshold_mode mode); /* main test function */ -static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval, - uint16_t sender_id, check_icmp_execution_mode mode, - time_t max_completion_time, struct timeval prog_start, ping_target **table, - unsigned short packets, check_icmp_socket_set sockset, - unsigned short number_of_targets, check_icmp_state *program_state); +static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id, + check_icmp_execution_mode mode, time_t max_completion_time, + struct timeval prog_start, ping_target **table, unsigned short packets, + check_icmp_socket_set sockset, unsigned short number_of_targets, + check_icmp_state *program_state); mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, check_icmp_threshold warn, check_icmp_threshold crit); @@ -297,14 +296,12 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.mode = MODE_ICMP; } else if (!strcmp(progname, "check_host")) { result.config.mode = MODE_HOSTCHECK; - result.config.pkt_interval = 1000000; result.config.number_of_packets = 5; result.config.crit.rta = result.config.warn.rta = 1000000; result.config.crit.pl = result.config.warn.pl = 100; } else if (!strcmp(progname, "check_rta_multi")) { result.config.mode = MODE_ALL; result.config.target_interval = 0; - result.config.pkt_interval = 50000; result.config.number_of_packets = 5; } /* support "--help" and "--version" */ @@ -418,13 +415,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { } } break; case 'i': { - get_timevar_wrapper parsed_time = get_timevar(optarg); - - if (parsed_time.error_code == OK) { - result.config.pkt_interval = parsed_time.time_range; - } else { - crash("failed to parse packet interval"); - } + // packet_interval was unused and is now removed } break; case 'I': { get_timevar_wrapper parsed_time = get_timevar(optarg); @@ -738,8 +729,8 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm } static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr, - time_t *pkt_interval, time_t *target_interval, - const uint16_t sender_id, ping_target **table, unsigned short packets, + time_t *target_interval, const uint16_t sender_id, + ping_target **table, unsigned short packets, const unsigned short number_of_targets, check_icmp_state *program_state) { struct icmp icmp_packet; @@ -799,7 +790,6 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad /* source quench means we're sending too fast, so increase the * interval and mark this packet lost */ if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) { - *pkt_interval = (unsigned int)((double)*pkt_interval * PACKET_BACKOFF_FACTOR); *target_interval = (unsigned int)((double)*target_interval * TARGET_BACKOFF_FACTOR); } else { program_state->targets_down++; @@ -931,18 +921,17 @@ int main(int argc, char **argv) { gettimeofday(&prog_start, NULL); time_t max_completion_time = - ((config.pkt_interval * config.number_of_targets * config.number_of_packets) + - (config.target_interval * config.number_of_targets)) + + (config.target_interval * config.number_of_targets) + (config.crit.rta * config.number_of_targets * config.number_of_packets) + config.crit.rta; if (debug) { printf("packets: %u, targets: %u\n" - "target_interval: %0.3f, pkt_interval %0.3f\n" + "target_interval: %0.3f\n" "crit.rta: %0.3f\n" "max_completion_time: %0.3f\n", config.number_of_packets, config.number_of_targets, - (float)config.target_interval / 1000, (float)config.pkt_interval / 1000, - (float)config.crit.rta / 1000, (float)max_completion_time / 1000); + (float)config.target_interval / 1000, (float)config.crit.rta / 1000, + (float)max_completion_time / 1000); } if (debug) { @@ -955,8 +944,7 @@ int main(int argc, char **argv) { if (debug) { printf("crit = {%ld, %u%%}, warn = {%ld, %u%%}\n", config.crit.rta, config.crit.pl, config.warn.rta, config.warn.pl); - printf("pkt_interval: %ld target_interval: %ld\n", config.pkt_interval, - config.target_interval); + printf("target_interval: %ld\n", config.target_interval); printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_pkt_size, timeout); } @@ -980,14 +968,13 @@ int main(int argc, char **argv) { target_index++; } - time_t pkt_interval = config.pkt_interval; time_t target_interval = config.target_interval; check_icmp_state program_state = check_icmp_state_init(); - run_checks(config.icmp_data_size, &pkt_interval, &target_interval, config.sender_id, - config.mode, max_completion_time, prog_start, table, config.number_of_packets, - sockset, config.number_of_targets, &program_state); + run_checks(config.icmp_data_size, &target_interval, config.sender_id, config.mode, + max_completion_time, prog_start, table, config.number_of_packets, sockset, + config.number_of_targets, &program_state); errno = 0; @@ -1006,7 +993,7 @@ int main(int argc, char **argv) { mp_exit(overall); } -static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval, +static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval, const uint16_t sender_id, const check_icmp_execution_mode mode, const time_t max_completion_time, const struct timeval prog_start, ping_target **table, const unsigned short packets, @@ -1039,17 +1026,15 @@ static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_ if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff(prog_start, prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { - wait_for_reply(sockset, *target_interval, icmp_pkt_size, pkt_interval, - target_interval, sender_id, table, packets, number_of_targets, - program_state); + wait_for_reply(sockset, *target_interval, icmp_pkt_size, target_interval, sender_id, + table, packets, number_of_targets, program_state); } } if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff_to_now(prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { - wait_for_reply(sockset, *pkt_interval * number_of_targets, icmp_pkt_size, pkt_interval, - target_interval, sender_id, table, packets, number_of_targets, - program_state); + wait_for_reply(sockset, number_of_targets, icmp_pkt_size, target_interval, sender_id, + table, packets, number_of_targets, program_state); } } @@ -1079,8 +1064,8 @@ static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_ if (targets_alive(number_of_targets, program_state->targets_down) || get_timevaldiff_to_now(prog_start) < max_completion_time || !(mode == MODE_HOSTCHECK && program_state->targets_down)) { - wait_for_reply(sockset, final_wait, icmp_pkt_size, pkt_interval, target_interval, - sender_id, table, packets, number_of_targets, program_state); + wait_for_reply(sockset, final_wait, icmp_pkt_size, target_interval, sender_id, table, + packets, number_of_targets, program_state); } } } @@ -1096,10 +1081,9 @@ static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_ * icmp echo reply : the rest */ static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval, - unsigned short icmp_pkt_size, time_t *pkt_interval, - time_t *target_interval, uint16_t sender_id, ping_target **table, - const unsigned short packets, const unsigned short number_of_targets, - check_icmp_state *program_state) { + unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id, + ping_target **table, const unsigned short packets, + const unsigned short number_of_targets, check_icmp_state *program_state) { union icmp_packet packet; if (!(packet.buf = malloc(icmp_pkt_size))) { crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); @@ -1185,8 +1169,8 @@ static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_inter printf("not a proper ICMP_ECHOREPLY\n"); } - handle_random_icmp(buf + hlen, &resp_addr, pkt_interval, target_interval, sender_id, - table, packets, number_of_targets, program_state); + handle_random_icmp(buf + hlen, &resp_addr, target_interval, sender_id, table, packets, + number_of_targets, program_state); continue; } @@ -2111,8 +2095,10 @@ void print_help(void) { printf(" %s", _("number of packets to send (default ")); printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS); printf(" %s\n", "-i"); - printf(" %s", _("max packet interval (default ")); + printf(" %s", _("[DEPRECATED] packet interval (default ")); printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000); + printf(" %s", _("This option was never actually used and is just mentioned here for " + "historical purposes")); printf(" %s\n", "-I"); printf(" %s%0.3fms)\n The time interval to wait in between one target and the next", _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000); diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 9acc96fd..0be0f2dc 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -35,7 +35,6 @@ check_icmp_config check_icmp_config_init() { .ttl = DEFAULT_TTL, .icmp_data_size = DEFAULT_PING_DATA_SIZE, .icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN, - .pkt_interval = DEFAULT_PKT_INTERVAL, .target_interval = 0, .number_of_packets = DEFAULT_NUMBER_OF_PACKETS, diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index 8092e343..1568d6c3 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -61,7 +61,6 @@ typedef struct { unsigned long ttl; unsigned short icmp_data_size; unsigned short icmp_pkt_size; - time_t pkt_interval; time_t target_interval; unsigned short number_of_packets; -- cgit v1.2.3-74-g34f1 From 7e9519e0ad730ede8fad0503102e1373b8999be2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:59:21 +0200 Subject: check_icmp: correct output strings --- plugins-root/check_icmp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 524e1fb9..3edfffa2 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -2279,10 +2279,10 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (rta >= crit.rta) { sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL); - xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double)crit.rta / 1000); + xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)crit.rta / 1000); } else if (rta >= warn.rta) { sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING); - xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double)warn.rta / 1000); + xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)warn.rta / 1000); } if (packet_loss < 100) { @@ -2319,10 +2319,10 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (packet_loss >= crit.pl) { sc_pl = mp_set_subcheck_state(sc_pl, STATE_CRITICAL); - xasprintf(&sc_pl.output, "%s > %u%%", sc_pl.output, crit.pl); + xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, crit.pl); } else if (packet_loss >= warn.pl) { sc_pl = mp_set_subcheck_state(sc_pl, STATE_WARNING); - xasprintf(&sc_pl.output, "%s > %u%%", sc_pl.output, crit.pl); + xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, warn.pl); } mp_perfdata pd_pl = perfdata_init(); @@ -2345,10 +2345,10 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (target.jitter >= crit.jitter) { sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_CRITICAL); - xasprintf(&sc_jitter.output, "%s > %0.3fms", sc_jitter.output, crit.jitter); + xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, crit.jitter); } else if (target.jitter >= warn.jitter) { sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_WARNING); - xasprintf(&sc_jitter.output, "%s > %0.3fms", sc_jitter.output, warn.jitter); + xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, warn.jitter); } if (packet_loss < 100) { @@ -2382,10 +2382,10 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (mos <= crit.mos) { sc_mos = mp_set_subcheck_state(sc_mos, STATE_CRITICAL); - xasprintf(&sc_mos.output, "%s < %0.1f", sc_mos.output, crit.mos); + xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, crit.mos); } else if (mos <= warn.mos) { sc_mos = mp_set_subcheck_state(sc_mos, STATE_WARNING); - xasprintf(&sc_mos.output, "%s < %0.1f", sc_mos.output, warn.mos); + xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, warn.mos); } if (packet_loss < 100) { @@ -2394,8 +2394,8 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, pd_mos.value = mp_create_pd_value(mos); pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos)); pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos)); - pd_mos.min = mp_create_pd_value(0); - pd_mos.max = mp_create_pd_value(5); + pd_mos.min = mp_create_pd_value(0); // MOS starts at 0 + pd_mos.max = mp_create_pd_value(5); // MOS max is 5, by definition mp_add_perfdata_to_subcheck(&sc_mos, pd_mos); } mp_add_subcheck_to_subcheck(&result, sc_mos); @@ -2408,10 +2408,10 @@ mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes, if (score <= crit.score) { sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL); - xasprintf(&sc_score.output, "%s < %f", sc_score.output, crit.score); + xasprintf(&sc_score.output, "%s <= %f", sc_score.output, crit.score); } else if (score <= warn.score) { sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING); - xasprintf(&sc_score.output, "%s < %f", sc_score.output, warn.score); + xasprintf(&sc_score.output, "%s <= %f", sc_score.output, warn.score); } if (packet_loss < 100) { -- cgit v1.2.3-74-g34f1 From 771ccfa52691e591be3736b6d03f36e69d82e464 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:41:24 +0200 Subject: check_icmp: remove unnecessary variable --- plugins-root/check_icmp.c | 3 +-- plugins-root/check_icmp.d/check_icmp_helpers.c | 1 - plugins-root/check_icmp.d/config.h | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 3edfffa2..be686ee3 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -407,7 +407,6 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) { result.config.icmp_data_size = (unsigned short)size; - result.config.icmp_pkt_size = (unsigned short)(size + ICMP_MINLEN); } else { usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data), @@ -945,7 +944,7 @@ int main(int argc, char **argv) { printf("crit = {%ld, %u%%}, warn = {%ld, %u%%}\n", config.crit.rta, config.crit.pl, config.warn.rta, config.warn.pl); printf("target_interval: %ld\n", config.target_interval); - printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_pkt_size, timeout); + printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_data_size + ICMP_MINLEN, timeout); } if (config.min_hosts_alive < -1) { diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c index 0be0f2dc..d56fbd8b 100644 --- a/plugins-root/check_icmp.d/check_icmp_helpers.c +++ b/plugins-root/check_icmp.d/check_icmp_helpers.c @@ -34,7 +34,6 @@ check_icmp_config check_icmp_config_init() { .ttl = DEFAULT_TTL, .icmp_data_size = DEFAULT_PING_DATA_SIZE, - .icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN, .target_interval = 0, .number_of_packets = DEFAULT_NUMBER_OF_PACKETS, diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index 1568d6c3..ab578ba5 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -60,7 +60,6 @@ typedef struct { unsigned long ttl; unsigned short icmp_data_size; - unsigned short icmp_pkt_size; time_t target_interval; unsigned short number_of_packets; -- cgit v1.2.3-74-g34f1 From bf67d5cb53e1810cb083ca72943375cbbc941acf Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:41:41 +0200 Subject: check_icmp: remove dead comment --- plugins-root/check_icmp.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index be686ee3..ca12b466 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -2127,14 +2127,6 @@ void print_help(void) { _("You can specify different RTA factors using the standardized abbreviations")); printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); - /* -d not yet implemented */ - /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 - hops")); printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent.")); printf - ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do - not."));*/ - /* printf ("%s\n", _("Long options are currently unsupported.")); - printf ("%s\n", _("Options marked with * require an argument")); - */ printf(UT_SUPPORT); } -- cgit v1.2.3-74-g34f1 From 622d7f4c89cdc76f23a5981590e306ebc0ae395f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:41:56 +0200 Subject: check_icmp: add long options to help --- plugins-root/check_icmp.c | 58 +++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index ca12b466..8feeb853 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -339,8 +339,9 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { {"number-of-packets", required_argument, 0, 'p'}, {"packet-interval", required_argument, 0, 'i'}, {"target-interval", required_argument, 0, 'I'}, + {"minimal-host-alive", required_argument, 0, 'm'}, {"outgoing-ttl", required_argument, 0, 'l'}, - {"packet_payload_size", required_argument, 0, 'b'}, + {"size", required_argument, 0, 'b'}, {"output-format", required_argument, 0, output_format_index}, {}, }; @@ -2062,56 +2063,59 @@ void print_help(void) { printf(UT_HELP_VRSN); printf(UT_EXTRA_OPTS); - printf(" %s\n", "-H"); + printf(" -H, --Host=HOST\n"); printf(" %s\n", - _("specify a target, might be one of: resolveable name, IPv6 address, IPv4 address " - "(necessary, can be given multiple times)")); - printf(" %s\n", "[-4|-6]"); + _("specify a target, might be one of: resolveable name | IPv6 address | IPv4 address\n" + " (required, can be given multiple times)")); + printf(" %s\n", "[-4|-6], [--ipv4-only|--ipv6-only]"); printf(" %s\n", _("Use IPv4 or IPv6 only to communicate with the targets")); - printf(" %s\n", "-w"); + printf(" %s\n", "-w, --warning=WARN_VALUE"); printf(" %s", _("warning threshold (default ")); printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL); - printf(" %s\n", "-c"); + printf(" %s\n", "-c, --critical=CRIT_VALUE"); printf(" %s", _("critical threshold (default ")); printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL); - printf(" %s\n", "-R"); + printf(" %s\n", "-R, --rta-mode-thresholds=RTA_THRESHOLDS"); printf(" %s\n", - _("RTA (round trip average) mode warning,critical, ex. 100ms,200ms unit in ms")); - printf(" %s\n", "-P"); + _("RTA (round trip average) mode warning,critical, ex. 100ms,200ms unit in ms")); + printf(" %s\n", "-P, --packet-loss-mode-thresholds=PACKET_LOSS_THRESHOLD"); printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); - printf(" %s\n", "-J"); + printf(" %s\n", "-J, --jitter-mode-thresholds=JITTER_MODE_THRESHOLD"); printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms ")); - printf(" %s\n", "-M"); + printf(" %s\n", "-M, --mos-mode-thresholds=MOS_MODE_THRESHOLD"); printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0")); - printf(" %s\n", "-S"); + printf(" %s\n", "-S, --score-mode-thresholds=SCORE_MODE_THRESHOLD"); printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 ")); - printf(" %s\n", "-O"); - printf(" %s\n", - _("detect out of order ICMP packts, if such packets are found, the result is CRITICAL")); - printf(" %s\n", "-n"); - printf(" %s\n", "-p"); + printf(" %s\n", "-O, --out-of-order-packets"); + printf( + " %s\n", + _("detect out of order ICMP packets, if such packets are found, the result is CRITICAL")); + printf(" %s\n", "[-n|-p], --number-of-packets=NUMBER_OF_PACKETS"); printf(" %s", _("number of packets to send (default ")); printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS); + printf(" %s\n", "-i"); printf(" %s", _("[DEPRECATED] packet interval (default ")); printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000); printf(" %s", _("This option was never actually used and is just mentioned here for " - "historical purposes")); - printf(" %s\n", "-I"); + "historical purposes\n")); + + printf(" %s\n", "-I, --target-interval=TARGET_INTERVAL"); printf(" %s%0.3fms)\n The time interval to wait in between one target and the next", _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000); - printf(" %s\n", "-m"); - printf(" %s", _("number of alive hosts required for success")); + printf(" %s\n", "-m, --minimal-host-alive=MIN_ALIVE"); + printf(" %s", _("number of alive hosts required for success. If less than MIN_ALIVE hosts " + "are OK, but MIN_ALIVE hosts are WARNING or OK, WARNING, else CRITICAL")); printf("\n"); - printf(" %s\n", "-l"); + printf(" %s\n", "-l, --outgoing-ttl=OUTGOING_TTL"); printf(" %s", _("TTL on outgoing packets (default ")); printf("%u)\n", DEFAULT_TTL); - printf(" %s\n", "-b"); - printf(" %s\n", _("Number of icmp data bytes to send")); - printf(" %s %lu + %d)\n", _("Packet size will be data bytes + icmp header (default"), + printf(" %s\n", "-b, --size=SIZE"); + printf(" %s\n", _("Number of icmp ping data bytes to send")); + printf(" %s %lu + %d)\n", _("Packet size will be SIZE + icmp header (default"), DEFAULT_PING_DATA_SIZE, ICMP_MINLEN); - printf(" %s\n", "-v"); + printf(" %s\n", "-v, --verbose"); printf(" %s\n", _("Verbosity, can be given multiple times (for debugging)")); printf(UT_OUTPUT_FORMAT); -- cgit v1.2.3-74-g34f1 From 6ac0580a10e5dcbda79ac8959a3b52f5217e877e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:42:25 +0200 Subject: check_icmp: Add comment to config --- plugins-root/check_icmp.d/config.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index ab578ba5..c348bef5 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -96,7 +96,9 @@ typedef struct icmp_ping_data { #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44) /* 80 msec packet interval by default */ -#define DEFAULT_PKT_INTERVAL 80000 +// DEPRECATED, remove when removing the option +#define DEFAULT_PKT_INTERVAL 80000 + #define DEFAULT_TARGET_INTERVAL 0 #define DEFAULT_WARN_RTA 200000 -- cgit v1.2.3-74-g34f1 From 8626d22fc5df98d73b36eedb01cb4ed02908ea18 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:44:46 +0200 Subject: check_icmp: trigger help before anything important --- plugins-root/check_icmp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 8feeb853..03924a7e 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -353,6 +353,7 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { long int arg; while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) { switch (arg) { + case '4': if (enforced_ai_family != AF_UNSPEC) { crash("Multiple protocol versions not supported"); @@ -369,6 +370,11 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { result.config.number_of_hosts++; break; } + case 'h': /* help */ + // Trigger help here to avoid adding hosts before that (and doing DNS queries) + print_help(); + exit(STATE_UNKNOWN); + break; case 'v': debug++; break; @@ -490,10 +496,6 @@ check_icmp_config_wrapper process_arguments(int argc, char **argv) { case 'V': /* version */ print_revision(progname, NP_VERSION); exit(STATE_UNKNOWN); - case 'h': /* help */ - print_help(); - exit(STATE_UNKNOWN); - break; case 'R': /* RTA mode */ { get_threshold2_wrapper rta_th = get_threshold2( optarg, strlen(optarg), result.config.warn, result.config.crit, const_rta_mode); -- cgit v1.2.3-74-g34f1 From bd55d9cd2d30deb2146d63af0c6ac1daa79cc496 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:46:45 +0200 Subject: check_icmp: Add missing line ending in help --- plugins-root/check_icmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 03924a7e..d46d2ccc 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -2104,7 +2104,7 @@ void print_help(void) { "historical purposes\n")); printf(" %s\n", "-I, --target-interval=TARGET_INTERVAL"); - printf(" %s%0.3fms)\n The time interval to wait in between one target and the next", + printf(" %s%0.3fms)\n The time interval to wait in between one target and the next\n", _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000); printf(" %s\n", "-m, --minimal-host-alive=MIN_ALIVE"); printf(" %s", _("number of alive hosts required for success. If less than MIN_ALIVE hosts " -- cgit v1.2.3-74-g34f1 From a0d3493f9dbe02f4cf1d578129581c4b45994ca2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 1 Apr 2025 00:05:18 +0200 Subject: check_dhcp: clang-format --- plugins-root/check_dhcp.c | 184 ++++++++++++++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 65 deletions(-) diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c index 6802232e..70809956 100644 --- a/plugins-root/check_dhcp.c +++ b/plugins-root/check_dhcp.c @@ -260,13 +260,15 @@ int main(int argc, char **argv) { dhcp_socket = create_dhcp_socket(); /* get hardware address of client machine */ - if (user_specified_mac != NULL) + if (user_specified_mac != NULL) { memcpy(client_hardware_address, user_specified_mac, 6); - else + } else { get_hardware_address(dhcp_socket, network_interface_name); + } - if (unicast) /* get IP address of client machine */ + if (unicast) { /* get IP address of client machine */ get_ip_address(dhcp_socket, network_interface_name); + } /* send DHCPDISCOVER packet */ send_dhcp_discover(dhcp_socket); @@ -358,8 +360,9 @@ static int get_hardware_address(int sock, char *interface_name) { int i; p = interface_name + strlen(interface_name) - 1; for (i = strlen(interface_name) - 1; i > 0; p--) { - if (isalpha(*p)) + if (isalpha(*p)) { break; + } } p++; if (p != interface_name) { @@ -393,8 +396,9 @@ static int get_hardware_address(int sock, char *interface_name) { exit(STATE_UNKNOWN); #endif - if (verbose) + if (verbose) { print_hardware_address(client_hardware_address); + } return OK; } @@ -419,8 +423,9 @@ static int get_ip_address(int sock, char *interface_name) { exit(STATE_UNKNOWN); #endif - if (verbose) + if (verbose) { printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); + } return OK; } @@ -484,8 +489,9 @@ static int send_dhcp_discover(int sock) { discover_packet.options[opts++] = (char)DHCP_OPTION_END; /* unicast fields */ - if (unicast) + if (unicast) { discover_packet.giaddr.s_addr = my_ip.s_addr; + } /* see RFC 1542, 4.1.1 */ discover_packet.hops = unicast ? 1 : 0; @@ -508,8 +514,9 @@ static int send_dhcp_discover(int sock) { /* send the DHCPDISCOVER packet out */ send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); - if (verbose) + if (verbose) { printf("\n\n"); + } return OK; } @@ -531,11 +538,13 @@ static int get_dhcp_offer(int sock) { for (responses = 0, valid_responses = 0;;) { time(¤t_time); - if ((current_time - start_time) >= dhcpoffer_timeout) + if ((current_time - start_time) >= dhcpoffer_timeout) { break; + } - if (verbose) + if (verbose) { printf("\n\n"); + } bzero(&source, sizeof(source)); bzero(&via, sizeof(via)); @@ -545,13 +554,15 @@ static int get_dhcp_offer(int sock) { result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); if (result != OK) { - if (verbose) + if (verbose) { printf(_("Result=ERROR\n")); + } continue; } else { - if (verbose) + if (verbose) { printf(_("Result=OK\n")); + } responses++; } @@ -568,30 +579,37 @@ static int get_dhcp_offer(int sock) { /* check packet xid to see if its the same as the one we used in the discover packet */ if (ntohl(offer_packet.xid) != packet_xid) { - if (verbose) - printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), packet_xid); + if (verbose) { + printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), + packet_xid); + } continue; } /* check hardware address */ result = OK; - if (verbose) + if (verbose) { printf("DHCPOFFER chaddr: "); + } for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { - if (verbose) + if (verbose) { printf("%02X", (unsigned char)offer_packet.chaddr[x]); + } - if (offer_packet.chaddr[x] != client_hardware_address[x]) + if (offer_packet.chaddr[x] != client_hardware_address[x]) { result = ERROR; + } } - if (verbose) + if (verbose) { printf("\n"); + } if (result == ERROR) { - if (verbose) + if (verbose) { printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n")); + } continue; } @@ -622,11 +640,13 @@ static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sock result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest)); - if (verbose) + if (verbose) { printf(_("send_dhcp_packet result: %d\n"), result); + } - if (result < 0) + if (result < 0) { return ERROR; + } return OK; } @@ -652,8 +672,9 @@ static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int time /* make sure some data has arrived */ if (!FD_ISSET(sock, &readfds)) { - if (verbose) + if (verbose) { printf(_("No (more) data received (nfound: %d)\n"), nfound); + } return ERROR; } @@ -661,8 +682,9 @@ static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int time bzero(&source_address, sizeof(source_address)); address_size = sizeof(source_address); recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); - if (verbose) + if (verbose) { printf("recv_result: %d\n", recv_result); + } if (recv_result == -1) { if (verbose) { @@ -706,8 +728,9 @@ static int create_dhcp_socket(void) { exit(STATE_UNKNOWN); } - if (verbose) + if (verbose) { printf("DHCP socket: %d\n", sock); + } /* set the reuse address flag so we don't get errors when restarting */ flag = 1; @@ -758,8 +781,9 @@ static int add_requested_server(struct in_addr server_address) { requested_server *new_server; new_server = (requested_server *)malloc(sizeof(requested_server)); - if (new_server == NULL) + if (new_server == NULL) { return ERROR; + } new_server->server_address = server_address; new_server->answered = false; @@ -769,8 +793,9 @@ static int add_requested_server(struct in_addr server_address) { requested_servers++; - if (verbose) + if (verbose) { printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); + } return OK; } @@ -783,14 +808,16 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { unsigned option_length; struct in_addr serv_ident = {0}; - if (offer_packet == NULL) + if (offer_packet == NULL) { return ERROR; + } /* process all DHCP options present in the packet */ for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { - if ((int)offer_packet->options[x] == -1) + if ((int)offer_packet->options[x] == -1) { break; + } /* get option type */ option_type = offer_packet->options[x++]; @@ -798,8 +825,9 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { /* get option length */ option_length = offer_packet->options[x++]; - if (verbose) + if (verbose) { printf("Option: %d (0x%02X)\n", option_type, option_length); + } /* get option data */ switch (option_type) { @@ -821,30 +849,35 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { } /* skip option data we're ignoring */ - if (option_type == 0) /* "pad" option, see RFC 2132 (3.1) */ + if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */ x += 1; - else + } else { x += option_length; + } } if (verbose) { - if (dhcp_lease_time == DHCP_INFINITE_TIME) + if (dhcp_lease_time == DHCP_INFINITE_TIME) { printf(_("Lease Time: Infinite\n")); - else + } else { printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time); - if (dhcp_renewal_time == DHCP_INFINITE_TIME) + } + if (dhcp_renewal_time == DHCP_INFINITE_TIME) { printf(_("Renewal Time: Infinite\n")); - else + } else { printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time); - if (dhcp_rebinding_time == DHCP_INFINITE_TIME) + } + if (dhcp_rebinding_time == DHCP_INFINITE_TIME) { printf(_("Rebinding Time: Infinite\n")); + } printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time); } new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); - if (new_offer == NULL) + if (new_offer == NULL) { return ERROR; + } /* * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the @@ -921,20 +954,23 @@ static int get_results(void) { for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { /* get max lease time we were offered */ - if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) + if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { max_lease_time = temp_offer->lease_time; + } /* see if we got the address we requested */ - if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) + if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) { received_requested_address = true; + } /* see if the servers we wanted a response from talked to us or not */ if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { if (verbose) { printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); - if (temp_server->answered) + if (temp_server->answered) { printf(_(" (duplicate)")); + } printf(_("\n")); } if (!temp_server->answered) { @@ -961,36 +997,41 @@ static int get_results(void) { for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { /* get max lease time we were offered */ - if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) + if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { max_lease_time = temp_offer->lease_time; + } /* see if we got the address we requested */ - if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) + if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) { received_requested_address = true; + } } } result = STATE_OK; - if (valid_responses == 0) + if (valid_responses == 0) { result = STATE_CRITICAL; - else if (requested_servers > 0 && requested_responses == 0) + } else if (requested_servers > 0 && requested_responses == 0) { result = STATE_CRITICAL; - else if (requested_responses < requested_servers) + } else if (requested_responses < requested_servers) { result = STATE_WARNING; - else if (request_specific_address && !received_requested_address) + } else if (request_specific_address && !received_requested_address) { result = STATE_WARNING; + } - if (exclusive && undesired_offer) + if (exclusive && undesired_offer) { result = STATE_CRITICAL; + } - if (result == 0) /* garrett honeycutt 2005 */ + if (result == 0) { /* garrett honeycutt 2005 */ printf("OK: "); - else if (result == 1) + } else if (result == 1) { printf("WARNING: "); - else if (result == 2) + } else if (result == 2) { printf("CRITICAL: "); - else if (result == 3) + } else if (result == 3) { printf("UNKNOWN: "); + } /* we didn't receive any DHCPOFFERs */ if (dhcp_offer_list == NULL) { @@ -1006,18 +1047,22 @@ static int get_results(void) { return result; } - if (requested_servers > 0) - printf(_(", %s%d of %d requested servers responded"), ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, + if (requested_servers > 0) { + printf(_(", %s%d of %d requested servers responded"), + ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, requested_servers); + } - if (request_specific_address) + if (request_specific_address) { printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); + } printf(_(", max lease time = ")); - if (max_lease_time == DHCP_INFINITE_TIME) + if (max_lease_time == DHCP_INFINITE_TIME) { printf(_("Infinity")); - else + } else { printf("%lu sec", (unsigned long)max_lease_time); + } printf(".\n"); @@ -1026,8 +1071,9 @@ static int get_results(void) { /* process command-line arguments */ static int process_arguments(int argc, char **argv) { - if (argc < 1) + if (argc < 1) { return ERROR; + } call_getopt(argc, argv); return validate_arguments(argc); @@ -1052,8 +1098,9 @@ static int call_getopt(int argc, char **argv) { while (true) { c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); - if (c == -1 || c == EOF || c == 1) + if (c == -1 || c == EOF || c == 1) { break; + } switch (c) { @@ -1072,8 +1119,9 @@ static int call_getopt(int argc, char **argv) { /* if(is_intnonneg(optarg)) */ - if (atoi(optarg) > 0) + if (atoi(optarg) > 0) { dhcpoffer_timeout = atoi(optarg); + } /* else usage("Time interval must be a nonnegative integer\n"); @@ -1082,10 +1130,12 @@ static int call_getopt(int argc, char **argv) { case 'm': /* MAC address */ - if ((user_specified_mac = mac_aton(optarg)) == NULL) + if ((user_specified_mac = mac_aton(optarg)) == NULL) { usage("Cannot parse MAC address.\n"); - if (verbose) + } + if (verbose) { print_hardware_address(user_specified_mac); + } break; @@ -1127,8 +1177,9 @@ static int call_getopt(int argc, char **argv) { static int validate_arguments(int argc) { - if (argc - optind > 0) + if (argc - optind > 0) { usage(_("Got unexpected non-option argument")); + } return OK; } @@ -1273,8 +1324,9 @@ static void resolve_host(const char *in, struct in_addr *out) { memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET; - if (getaddrinfo(in, NULL, &hints, &ai) != 0) + if (getaddrinfo(in, NULL, &hints, &ai) != 0) { usage_va(_("Invalid hostname/address - %s"), optarg); + } memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); freeaddrinfo(ai); @@ -1288,8 +1340,9 @@ static unsigned char *mac_aton(const char *string) { for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { /* ignore ':' and any other non-hex character */ - if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) + if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) { continue; + } tmp[0] = string[i]; tmp[1] = string[i + 1]; tmp[2] = '\0'; @@ -1305,8 +1358,9 @@ static void print_hardware_address(const unsigned char *address) { int i; printf(_("Hardware address: ")); - for (i = 0; i < 5; i++) + for (i = 0; i < 5; i++) { printf("%2.2x:", address[i]); + } printf("%2.2x", address[i]); putchar('\n'); } -- cgit v1.2.3-74-g34f1 From 0453d6d1e206c1f4c156bf73c473608a8c0da4f5 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 1 Apr 2025 00:44:26 +0200 Subject: Refactor check_dhcp --- plugins-root/check_dhcp.c | 741 ++++++++++++++++++++----------------- plugins-root/check_dhcp.d/config.h | 50 +++ 2 files changed, 448 insertions(+), 343 deletions(-) create mode 100644 plugins-root/check_dhcp.d/config.h diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c index 70809956..3732d970 100644 --- a/plugins-root/check_dhcp.c +++ b/plugins-root/check_dhcp.c @@ -4,7 +4,7 @@ * * License: GPL * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) - * Copyright (c) 2001-2023 Monitoring Plugins Development Team + * Copyright (c) 2001-2025 Monitoring Plugins Development Team * * Description: * @@ -34,13 +34,17 @@ *****************************************************************************/ const char *progname = "check_dhcp"; -const char *copyright = "2001-2024"; +const char *copyright = "2001-2025"; const char *email = "devel@monitoring-plugins.org"; -#include "common.h" -#include "netutils.h" -#include "utils.h" +#include "../plugins/common.h" +#include "../plugins/utils.h" +#include "./check_dhcp.d/config.h" +#include "../lib/output.h" +#include "../lib/utils_base.h" +#include "states.h" +#include #include #include #include @@ -111,8 +115,9 @@ static long mac_addr_dlpi(const char *, int, u_char *); /**** Common definitions ****/ -#define OK 0 -#define ERROR -1 +#define OK 0 +#define ERROR -1 +#define MAC_ADDR_LEN 6 /**** DHCP definitions ****/ @@ -149,12 +154,6 @@ typedef struct dhcp_offer_struct { struct dhcp_offer_struct *next; } dhcp_offer; -typedef struct requested_server_struct { - struct in_addr server_address; - bool answered; - struct requested_server_struct *next; -} requested_server; - #define BOOTREQUEST 1 #define BOOTREPLY 2 @@ -186,65 +185,60 @@ typedef struct requested_server_struct { #define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ #define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ -static bool unicast = false; /* unicast mode: mimic a DHCP relay */ -static bool exclusive = false; /* exclusive mode aka "rogue DHCP server detection" */ -static struct in_addr my_ip; /* our address (required for relay) */ -static struct in_addr dhcp_ip; /* server to query (if in unicast mode) */ -static unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = ""; -static unsigned char *user_specified_mac = NULL; - -static char network_interface_name[IFNAMSIZ] = "eth0"; - -static uint32_t packet_xid = 0; - -static uint32_t dhcp_lease_time = 0; -static uint32_t dhcp_renewal_time = 0; -static uint32_t dhcp_rebinding_time = 0; - -static int dhcpoffer_timeout = 2; - -static dhcp_offer *dhcp_offer_list = NULL; -static requested_server *requested_server_list = NULL; - -static int valid_responses = 0; /* number of valid DHCPOFFERs we received */ -static int requested_servers = 0; -static int requested_responses = 0; - -static bool request_specific_address = false; -static bool received_requested_address = false; static int verbose = 0; -static struct in_addr requested_address; -static int process_arguments(int, char **); -static int call_getopt(int, char **); -static int validate_arguments(int); +typedef struct process_arguments_wrapper { + int error; + check_dhcp_config config; +} process_arguments_wrapper; + +static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); void print_usage(void); static void print_help(void); -static void resolve_host(const char *in, struct in_addr *out); -static unsigned char *mac_aton(const char *); -static void print_hardware_address(const unsigned char *); -static int get_hardware_address(int, char *); -static int get_ip_address(int, char *); - -static int send_dhcp_discover(int); -static int get_dhcp_offer(int); - -static int get_results(void); - -static int add_dhcp_offer(struct in_addr, dhcp_packet *); -static int free_dhcp_offer_list(void); -static int free_requested_server_list(void); - -static int create_dhcp_socket(void); -static int close_dhcp_socket(int); -static int send_dhcp_packet(void *, int, int, struct sockaddr_in *); -static int receive_dhcp_packet(void *, int, int, int, struct sockaddr_in *); +static void resolve_host(const char * /*in*/, struct in_addr * /*out*/); +static unsigned char *mac_aton(const char * /*string*/); +static void print_hardware_address(const unsigned char * /*address*/); +static int get_hardware_address(int /*sock*/, char * /*interface_name*/, unsigned char *client_hardware_address); + +typedef struct get_ip_address_wrapper { + int error; + struct in_addr my_ip; +} get_ip_address_wrapper; +static get_ip_address_wrapper get_ip_address(int /*sock*/, char * /*interface_name*/); + +typedef struct send_dhcp_discover_wrapper { + int error; + uint32_t packet_xid; +} send_dhcp_discover_wrapper; +static send_dhcp_discover_wrapper send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address, + bool request_specific_address, struct in_addr my_ip, + unsigned char *client_hardware_address); +typedef struct get_dhcp_offer_wrapper { + int error; + int valid_responses; + dhcp_offer *dhcp_offer_list; +} get_dhcp_offer_wrapper; +static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list, + const unsigned char *client_hardware_address); + +static mp_subcheck get_results(bool exclusive, int requested_servers, struct in_addr requested_address, bool request_specific_address, + requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list); + +typedef struct add_dhcp_offer_wrapper { + int error; + dhcp_offer *dhcp_offer_list; +} add_dhcp_offer_wrapper; +static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/, dhcp_packet * /*offer_packet*/, dhcp_offer *dhcp_offer_list); +static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list); +static int free_requested_server_list(requested_server *requested_server_list); + +static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name); +static int close_dhcp_socket(int /*sock*/); +static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, struct sockaddr_in * /*dest*/); +static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, int /*timeout*/, struct sockaddr_in * /*address*/); int main(int argc, char **argv) { - int dhcp_socket; - int result = STATE_UNKNOWN; - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); @@ -252,45 +246,80 @@ int main(int argc, char **argv) { /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); - if (process_arguments(argc, argv) != OK) { + process_arguments_wrapper tmp = process_arguments(argc, argv); + + if (tmp.error != OK) { usage4(_("Could not parse arguments")); } + check_dhcp_config config = tmp.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* create socket for DHCP communications */ - dhcp_socket = create_dhcp_socket(); + int dhcp_socket = create_dhcp_socket(config.unicast_mode, config.network_interface_name); /* get hardware address of client machine */ - if (user_specified_mac != NULL) { - memcpy(client_hardware_address, user_specified_mac, 6); + unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = ""; + if (config.user_specified_mac != NULL) { + memcpy(client_hardware_address, config.user_specified_mac, MAC_ADDR_LEN); } else { - get_hardware_address(dhcp_socket, network_interface_name); + get_hardware_address(dhcp_socket, config.network_interface_name, client_hardware_address); } - if (unicast) { /* get IP address of client machine */ - get_ip_address(dhcp_socket, network_interface_name); + struct in_addr my_ip = {0}; + + if (config.unicast_mode) { /* get IP address of client machine */ + get_ip_address_wrapper tmp_get_ip = get_ip_address(dhcp_socket, config.network_interface_name); + if (tmp_get_ip.error == OK) { + my_ip = tmp_get_ip.my_ip; + } else { + // TODO failed to get own IP + die(STATE_UNKNOWN, "Failed to retrieve my own IP address in unicast mode"); + } } /* send DHCPDISCOVER packet */ - send_dhcp_discover(dhcp_socket); + send_dhcp_discover_wrapper disco_res = send_dhcp_discover(dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address, + config.request_specific_address, my_ip, client_hardware_address); + + if (disco_res.error != OK) { + // DO something? + die(STATE_UNKNOWN, "Failed to send DHCP discover"); + } /* wait for a DHCPOFFER packet */ - get_dhcp_offer(dhcp_socket); + get_dhcp_offer_wrapper offer_res = + get_dhcp_offer(dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address); + + int valid_responses = 0; + dhcp_offer *dhcp_offer_list = NULL; + if (offer_res.error == OK) { + valid_responses = offer_res.valid_responses; + dhcp_offer_list = offer_res.dhcp_offer_list; + } else { + die(STATE_UNKNOWN, "Failed to get DHCP offers"); + } /* close socket we created */ close_dhcp_socket(dhcp_socket); - /* determine state/plugin output to return */ - result = get_results(); + mp_check overall = mp_check_init(); + /* determine state/plugin output to return */ + mp_subcheck sc_res = get_results(config.exclusive_mode, config.num_of_requested_servers, config.requested_address, + config.request_specific_address, config.requested_server_list, valid_responses, dhcp_offer_list); + mp_add_subcheck_to_check(&overall, sc_res); /* free allocated memory */ - free_dhcp_offer_list(); - free_requested_server_list(); + free_dhcp_offer_list(dhcp_offer_list); + free_requested_server_list(config.requested_server_list); - return result; + mp_exit(overall); } /* determines hardware address on client machine */ -static int get_hardware_address(int sock, char *interface_name) { +int get_hardware_address(int sock, char *interface_name, unsigned char *client_hardware_address) { #if defined(__linux__) struct ifreq ifr; @@ -304,7 +333,7 @@ static int get_hardware_address(int sock, char *interface_name) { exit(STATE_UNKNOWN); } - memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); + memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN); #elif defined(__bsd__) /* King 2004 see ACKNOWLEDGEMENTS */ @@ -404,7 +433,7 @@ static int get_hardware_address(int sock, char *interface_name) { } /* determines IP address of the client interface */ -static int get_ip_address(int sock, char *interface_name) { +get_ip_address_wrapper get_ip_address(int sock, char *interface_name) { #if defined(SIOCGIFADDR) struct ifreq ifr; @@ -416,29 +445,28 @@ static int get_ip_address(int sock, char *interface_name) { exit(STATE_UNKNOWN); } - my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; - #else printf(_("Error: Cannot get interface IP address on this platform.\n")); exit(STATE_UNKNOWN); #endif + get_ip_address_wrapper result = { + .error = OK, + .my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, + }; + if (verbose) { - printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); + printf(_("Pretending to be relay client %s\n"), inet_ntoa(result.my_ip)); } - return OK; + return result; } /* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ -static int send_dhcp_discover(int sock) { - dhcp_packet discover_packet; - struct sockaddr_in sockaddr_broadcast; - unsigned short opts; - - /* clear the packet data structure */ - bzero(&discover_packet, sizeof(discover_packet)); - +static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address, + bool request_specific_address, struct in_addr my_ip, + unsigned char *client_hardware_address) { + dhcp_packet discover_packet = {0}; /* boot request flag (backward compatible with BOOTP servers) */ discover_packet.op = BOOTREQUEST; @@ -448,12 +476,15 @@ static int send_dhcp_discover(int sock) { /* length of our hardware address */ discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; + send_dhcp_discover_wrapper result = { + .error = OK, + }; /* * transaction ID is supposed to be random. */ srand(time(NULL) ^ getpid()); - packet_xid = random(); - discover_packet.xid = htonl(packet_xid); + result.packet_xid = random(); + discover_packet.xid = htonl(result.packet_xid); /*discover_packet.secs=htons(65535);*/ discover_packet.secs = 0xFF; @@ -473,7 +504,7 @@ static int send_dhcp_discover(int sock) { discover_packet.options[2] = '\x53'; discover_packet.options[3] = '\x63'; - opts = 4; + unsigned short opts = 4; /* DHCP message type is embedded in options field */ discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ @@ -497,10 +528,11 @@ static int send_dhcp_discover(int sock) { discover_packet.hops = unicast ? 1 : 0; /* send the DHCPDISCOVER packet to broadcast address */ - sockaddr_broadcast.sin_family = AF_INET; - sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); - sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST; - bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); + struct sockaddr_in sockaddr_broadcast = { + .sin_family = AF_INET, + .sin_port = htons(DHCP_SERVER_PORT), + .sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST, + }; if (verbose) { printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); @@ -518,25 +550,21 @@ static int send_dhcp_discover(int sock) { printf("\n\n"); } - return OK; + return result; } /* waits for a DHCPOFFER message from one or more DHCP servers */ -static int get_dhcp_offer(int sock) { - dhcp_packet offer_packet; - struct sockaddr_in source; - struct sockaddr_in via; - int result = OK; - int responses = 0; - int x; +get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list, + const unsigned char *client_hardware_address) { time_t start_time; - time_t current_time; - time(&start_time); + int result = OK; + int responses = 0; + int valid_responses = 0; /* receive as many responses as we can */ - for (responses = 0, valid_responses = 0;;) { - + for (;;) { + time_t current_time; time(¤t_time); if ((current_time - start_time) >= dhcpoffer_timeout) { break; @@ -546,9 +574,8 @@ static int get_dhcp_offer(int sock) { printf("\n\n"); } - bzero(&source, sizeof(source)); - bzero(&via, sizeof(via)); - bzero(&offer_packet, sizeof(offer_packet)); + struct sockaddr_in source = {0}; + dhcp_packet offer_packet = {0}; result = OK; result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); @@ -559,16 +586,16 @@ static int get_dhcp_offer(int sock) { } continue; - } else { - if (verbose) { - printf(_("Result=OK\n")); - } - - responses++; } + if (verbose) { + printf(_("Result=OK\n")); + } + + responses++; /* The "source" is either a server or a relay. */ /* Save a copy of "source" into "via" even if it's via itself */ + struct sockaddr_in via = {0}; memcpy(&via, &source, sizeof(source)); if (verbose) { @@ -593,12 +620,12 @@ static int get_dhcp_offer(int sock) { printf("DHCPOFFER chaddr: "); } - for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { + for (int i = 0; i < ETHERNET_HARDWARE_ADDRESS_LENGTH; i++) { if (verbose) { - printf("%02X", (unsigned char)offer_packet.chaddr[x]); + printf("%02X", offer_packet.chaddr[i]); } - if (offer_packet.chaddr[x] != client_hardware_address[x]) { + if (offer_packet.chaddr[i] != client_hardware_address[i]) { result = ERROR; } } @@ -621,7 +648,12 @@ static int get_dhcp_offer(int sock) { printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); } - add_dhcp_offer(source.sin_addr, &offer_packet); + add_dhcp_offer_wrapper add_res = add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list); + if (add_res.error != OK) { + // TODO + } else { + dhcp_offer_list = add_res.dhcp_offer_list; + } valid_responses++; } @@ -631,14 +663,17 @@ static int get_dhcp_offer(int sock) { printf(_("Valid responses for this machine: %d\n"), valid_responses); } - return OK; + get_dhcp_offer_wrapper ret_val = { + .error = OK, + .valid_responses = valid_responses, + .dhcp_offer_list = dhcp_offer_list, + }; + return ret_val; } /* sends a DHCP packet */ -static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { - int result; - - result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest)); +int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { + int result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest)); if (verbose) { printf(_("send_dhcp_packet result: %d\n"), result); @@ -652,23 +687,19 @@ static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sock } /* receives a DHCP packet */ -static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { - struct timeval tv; - fd_set readfds; - fd_set oobfds; - int recv_result; - socklen_t address_size; - struct sockaddr_in source_address; - int nfound; - +int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { /* wait for data to arrive (up time timeout) */ - tv.tv_sec = timeout; - tv.tv_usec = 0; + struct timeval timeout_val = { + .tv_sec = timeout, + .tv_usec = 0, + }; + fd_set readfds; FD_ZERO(&readfds); + fd_set oobfds; FD_ZERO(&oobfds); FD_SET(sock, &readfds); FD_SET(sock, &oobfds); - nfound = select(sock + 1, &readfds, NULL, &oobfds, &tv); + int nfound = select(sock + 1, &readfds, NULL, &oobfds, &timeout_val); /* make sure some data has arrived */ if (!FD_ISSET(sock, &readfds)) { @@ -678,51 +709,44 @@ static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int time return ERROR; } - else { - bzero(&source_address, sizeof(source_address)); - address_size = sizeof(source_address); - recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); - if (verbose) { - printf("recv_result: %d\n", recv_result); - } - - if (recv_result == -1) { - if (verbose) { - printf(_("recvfrom() failed, ")); - printf("errno: (%d) -> %s\n", errno, strerror(errno)); - } - return ERROR; - } else { - if (verbose) { - printf(_("receive_dhcp_packet() result: %d\n"), recv_result); - printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr)); - } + struct sockaddr_in source_address = {0}; + socklen_t address_size = sizeof(source_address); + int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); + if (verbose) { + printf("recv_result: %d\n", recv_result); + } - memcpy(address, &source_address, sizeof(source_address)); - return OK; + if (recv_result == -1) { + if (verbose) { + printf(_("recvfrom() failed, ")); + printf("errno: (%d) -> %s\n", errno, strerror(errno)); } + return ERROR; + } + if (verbose) { + printf(_("receive_dhcp_packet() result: %d\n"), recv_result); + printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr)); } + memcpy(address, &source_address, sizeof(source_address)); return OK; } /* creates a socket for DHCP communication */ -static int create_dhcp_socket(void) { - struct sockaddr_in myname; - struct ifreq interface; - int sock; - int flag = 1; - +int create_dhcp_socket(bool unicast, char *network_interface_name) { /* Set up the address we're going to bind to. */ - bzero(&myname, sizeof(myname)); - myname.sin_family = AF_INET; /* listen to DHCP server port if we're in unicast mode */ - myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT); - myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY; - bzero(&myname.sin_zero, sizeof(myname.sin_zero)); + struct sockaddr_in myname = { + .sin_family = AF_INET, + .sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT), + // TODO previously the next line was trying to use our own IP, we was not set + // until some point later, so it was removed. Recheck whether it is actually + // necessary/useful + .sin_addr.s_addr = INADDR_ANY, + }; /* create a socket for DHCP communications */ - sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { printf(_("Error: Could not create socket!\n")); exit(STATE_UNKNOWN); @@ -733,7 +757,7 @@ static int create_dhcp_socket(void) { } /* set the reuse address flag so we don't get errors when restarting */ - flag = 1; + int flag = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { printf(_("Error: Could not set reuse address option on DHCP socket!\n")); exit(STATE_UNKNOWN); @@ -745,6 +769,7 @@ static int create_dhcp_socket(void) { exit(STATE_UNKNOWN); } + struct ifreq interface; /* bind socket to interface */ #if defined(__linux__) strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); @@ -769,18 +794,14 @@ static int create_dhcp_socket(void) { } /* closes DHCP socket */ -static int close_dhcp_socket(int sock) { - +int close_dhcp_socket(int sock) { close(sock); - return OK; } /* adds a requested server address to list in memory */ -static int add_requested_server(struct in_addr server_address) { - requested_server *new_server; - - new_server = (requested_server *)malloc(sizeof(requested_server)); +int add_requested_server(struct in_addr server_address, int *requested_servers, requested_server **requested_server_list) { + requested_server *new_server = (requested_server *)malloc(sizeof(requested_server)); if (new_server == NULL) { return ERROR; } @@ -788,10 +809,10 @@ static int add_requested_server(struct in_addr server_address) { new_server->server_address = server_address; new_server->answered = false; - new_server->next = requested_server_list; - requested_server_list = new_server; + new_server->next = *requested_server_list; + *requested_server_list = new_server; - requested_servers++; + *requested_servers += 1; if (verbose) { printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); @@ -801,29 +822,31 @@ static int add_requested_server(struct in_addr server_address) { } /* adds a DHCP OFFER to list in memory */ -static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { - dhcp_offer *new_offer; - int x; - unsigned option_type; - unsigned option_length; - struct in_addr serv_ident = {0}; - +add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet, dhcp_offer *dhcp_offer_list) { if (offer_packet == NULL) { - return ERROR; + add_dhcp_offer_wrapper tmp = { + .error = ERROR, + }; + return tmp; } + uint32_t dhcp_lease_time = 0; + uint32_t dhcp_renewal_time = 0; + uint32_t dhcp_rebinding_time = 0; + dhcp_offer *new_offer; + struct in_addr serv_ident = {0}; /* process all DHCP options present in the packet */ - for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { + for (int dchp_opt_idx = 4; dchp_opt_idx < MAX_DHCP_OPTIONS_LENGTH - 1;) { - if ((int)offer_packet->options[x] == -1) { + if ((int)offer_packet->options[dchp_opt_idx] == -1) { break; } /* get option type */ - option_type = offer_packet->options[x++]; + unsigned option_type = offer_packet->options[dchp_opt_idx++]; /* get option length */ - option_length = offer_packet->options[x++]; + unsigned option_length = offer_packet->options[dchp_opt_idx++]; if (verbose) { printf("Option: %d (0x%02X)\n", option_type, option_length); @@ -832,27 +855,27 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { /* get option data */ switch (option_type) { case DHCP_OPTION_LEASE_TIME: - memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); + memcpy(&dhcp_lease_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_lease_time)); dhcp_lease_time = ntohl(dhcp_lease_time); break; case DHCP_OPTION_RENEWAL_TIME: - memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); + memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_renewal_time)); dhcp_renewal_time = ntohl(dhcp_renewal_time); break; case DHCP_OPTION_REBINDING_TIME: - memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); + memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_rebinding_time)); dhcp_rebinding_time = ntohl(dhcp_rebinding_time); break; case DHCP_OPTION_SERVER_IDENTIFIER: - memcpy(&serv_ident.s_addr, &offer_packet->options[x], sizeof(serv_ident.s_addr)); + memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx], sizeof(serv_ident.s_addr)); break; } /* skip option data we're ignoring */ if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */ - x += 1; + dchp_opt_idx += 1; } else { - x += option_length; + dchp_opt_idx += option_length; } } @@ -876,7 +899,10 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); if (new_offer == NULL) { - return ERROR; + add_dhcp_offer_wrapper tmp = { + .error = ERROR, + }; + return tmp; } /* @@ -907,15 +933,18 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { new_offer->next = dhcp_offer_list; dhcp_offer_list = new_offer; - return OK; + add_dhcp_offer_wrapper result = { + .error = OK, + .dhcp_offer_list = dhcp_offer_list, + }; + + return result; } /* frees memory allocated to DHCP OFFER list */ -static int free_dhcp_offer_list(void) { - dhcp_offer *this_offer; +int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) { dhcp_offer *next_offer; - - for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) { + for (dhcp_offer *this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) { next_offer = this_offer->next; free(this_offer); } @@ -924,11 +953,9 @@ static int free_dhcp_offer_list(void) { } /* frees memory allocated to requested server list */ -static int free_requested_server_list(void) { - requested_server *this_server; +int free_requested_server_list(requested_server *requested_server_list) { requested_server *next_server; - - for (this_server = requested_server_list; this_server != NULL; this_server = next_server) { + for (requested_server *this_server = requested_server_list; this_server != NULL; this_server = next_server) { next_server = this_server->next; free(this_server); } @@ -937,22 +964,39 @@ static int free_requested_server_list(void) { } /* gets state and plugin output to return */ -static int get_results(void) { - dhcp_offer *temp_offer, *undesired_offer = NULL; - requested_server *temp_server; - int result; - uint32_t max_lease_time = 0; - - received_requested_address = false; +mp_subcheck get_results(bool exclusive, const int requested_servers, const struct in_addr requested_address, bool request_specific_address, + requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list) { + mp_subcheck sc_dhcp_results = mp_subcheck_init(); + sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK); - /* checks responses from requested servers */ - requested_responses = 0; - if (requested_servers > 0) { + /* we didn't receive any DHCPOFFERs */ + if (dhcp_offer_list == NULL) { + sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL); + xasprintf(&sc_dhcp_results.output, "%s", "No DHCP offers were received"); + return sc_dhcp_results; + } - for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { + if (valid_responses == 0) { + // No valid responses at all, so early exit here + sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL); + xasprintf(&sc_dhcp_results.output, "No valid responses received"); + return sc_dhcp_results; + } - for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + if (valid_responses == 1) { + xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFER", valid_responses); + } else { + xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFERs", valid_responses); + } + bool received_requested_address = false; + dhcp_offer *undesired_offer = NULL; + uint32_t max_lease_time = 0; + /* checks responses from requested servers */ + int requested_responses = 0; + if (requested_servers > 0) { + for (requested_server *temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { + for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { /* get max lease time we were offered */ if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { max_lease_time = temp_offer->lease_time; @@ -963,7 +1007,7 @@ static int get_results(void) { received_requested_address = true; } - /* see if the servers we wanted a response from talked to us or not */ + /* see if the servers we wanted a response from, talked to us or not */ if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { if (verbose) { printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); @@ -973,6 +1017,7 @@ static int get_results(void) { } printf(_("\n")); } + if (!temp_server->answered) { requested_responses++; temp_server->answered = true; @@ -983,19 +1028,32 @@ static int get_results(void) { } /* exclusive mode: check for undesired offers */ - for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { if (!temp_offer->desired) { undesired_offer = temp_offer; /* Checks only for the first undesired offer */ break; /* no further checks needed */ } } - } - /* else check and see if we got our requested address from any server */ - else { + mp_subcheck sc_rqust_srvs = mp_subcheck_init(); + xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded", requested_responses, requested_servers); - for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + if (requested_responses == requested_servers) { + sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK); + } else if (requested_responses == 0) { + sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_CRITICAL); + } else if (requested_responses < requested_servers) { + sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING); + } else { + // We received more(!) responses than we asked for? + // This case shouldn't happen, but is here for completion + sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING); + } + mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqust_srvs); + } else { + /* else check and see if we got our requested address from any server */ + for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { /* get max lease time we were offered */ if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { max_lease_time = temp_offer->lease_time; @@ -1008,79 +1066,77 @@ static int get_results(void) { } } - result = STATE_OK; - if (valid_responses == 0) { - result = STATE_CRITICAL; - } else if (requested_servers > 0 && requested_responses == 0) { - result = STATE_CRITICAL; - } else if (requested_responses < requested_servers) { - result = STATE_WARNING; - } else if (request_specific_address && !received_requested_address) { - result = STATE_WARNING; + if (max_lease_time == DHCP_INFINITE_TIME) { + xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output); + } else { + xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds", sc_dhcp_results.output, max_lease_time); } - if (exclusive && undesired_offer) { - result = STATE_CRITICAL; - } + if (exclusive) { + mp_subcheck sc_rogue_server = mp_subcheck_init(); - if (result == 0) { /* garrett honeycutt 2005 */ - printf("OK: "); - } else if (result == 1) { - printf("WARNING: "); - } else if (result == 2) { - printf("CRITICAL: "); - } else if (result == 3) { - printf("UNKNOWN: "); - } + if (undesired_offer != NULL) { + // We wanted to get a DHCPOFFER exclusively from one machine, but another one + // sent one (too) + sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_CRITICAL); - /* we didn't receive any DHCPOFFERs */ - if (dhcp_offer_list == NULL) { - printf(_("No DHCPOFFERs were received.\n")); - return result; - } + // Get the addresses for printout + // 1.address of the sending server + char server_address[INET_ADDRSTRLEN]; + const char *server_address_transformed = + inet_ntop(AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address)); + + if (server_address != server_address_transformed) { + die(STATE_UNKNOWN, "inet_ntop failed"); + } - printf(_("Received %d DHCPOFFER(s)"), valid_responses); + // 2.address offered + char offered_address[INET_ADDRSTRLEN]; + const char *offered_address_transformed = + inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address, sizeof(offered_address)); - if (exclusive && undesired_offer) { - printf(_(", Rogue DHCP Server detected! Server %s"), inet_ntoa(undesired_offer->server_address)); - printf(_(" offered %s \n"), inet_ntoa(undesired_offer->offered_address)); - return result; - } + if (offered_address != offered_address_transformed) { + die(STATE_UNKNOWN, "inet_ntop failed"); + } - if (requested_servers > 0) { - printf(_(", %s%d of %d requested servers responded"), - ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, - requested_servers); + xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s", server_address, offered_address); + } else { + sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK); + xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected"); + } + mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rogue_server); } if (request_specific_address) { - printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); - } + mp_subcheck sc_rqustd_addr = mp_subcheck_init(); - printf(_(", max lease time = ")); - if (max_lease_time == DHCP_INFINITE_TIME) { - printf(_("Infinity")); - } else { - printf("%lu sec", (unsigned long)max_lease_time); - } + if (received_requested_address) { + sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK); + xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered", inet_ntoa(requested_address)); + } else { + sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING); + xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered", inet_ntoa(requested_address)); + } - printf(".\n"); + mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr); + } - return result; + return sc_dhcp_results; } /* process command-line arguments */ -static int process_arguments(int argc, char **argv) { +process_arguments_wrapper process_arguments(int argc, char **argv) { if (argc < 1) { - return ERROR; + process_arguments_wrapper tmp = { + .error = ERROR, + }; + return tmp; } - call_getopt(argc, argv); - return validate_arguments(argc); -} + enum { + output_format_index = CHAR_MAX + 1, + }; -static int call_getopt(int argc, char **argv) { - extern int optind; int option_index = 0; static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, {"requestedip", required_argument, 0, 'r'}, @@ -1092,65 +1148,55 @@ static int call_getopt(int argc, char **argv) { {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; - int c = 0; + check_dhcp_config config = check_dhcp_config_init(); + int option_char = 0; while (true) { - c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); + option_char = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); - if (c == -1 || c == EOF || c == 1) { + if (option_char == -1 || option_char == EOF || option_char == 1) { break; } - switch (c) { - + switch (option_char) { case 's': /* DHCP server address */ - resolve_host(optarg, &dhcp_ip); - add_requested_server(dhcp_ip); + resolve_host(optarg, &config.dhcp_ip); + add_requested_server(config.dhcp_ip, &config.num_of_requested_servers, &config.requested_server_list); break; case 'r': /* address we are requested from DHCP servers */ - resolve_host(optarg, &requested_address); - request_specific_address = true; + resolve_host(optarg, &config.requested_address); + config.request_specific_address = true; break; case 't': /* timeout */ - - /* - if(is_intnonneg(optarg)) - */ if (atoi(optarg) > 0) { - dhcpoffer_timeout = atoi(optarg); + config.dhcpoffer_timeout = atoi(optarg); } - /* - else - usage("Time interval must be a nonnegative integer\n"); - */ break; case 'm': /* MAC address */ - - if ((user_specified_mac = mac_aton(optarg)) == NULL) { + if ((config.user_specified_mac = mac_aton(optarg)) == NULL) { usage("Cannot parse MAC address.\n"); } if (verbose) { - print_hardware_address(user_specified_mac); + print_hardware_address(config.user_specified_mac); } - break; case 'i': /* interface name */ - - strncpy(network_interface_name, optarg, sizeof(network_interface_name) - 1); - network_interface_name[sizeof(network_interface_name) - 1] = '\x0'; - + strncpy(config.network_interface_name, optarg, sizeof(config.network_interface_name) - 1); + config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0'; break; case 'u': /* unicast testing */ - unicast = true; + config.unicast_mode = true; break; + case 'x': /* exclusive testing aka "rogue DHCP server detection" */ - exclusive = true; + config.exclusive_mode = true; break; case 'V': /* version */ @@ -1164,6 +1210,18 @@ static int call_getopt(int argc, char **argv) { case 'v': /* verbose */ verbose = 1; break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + config.output_format_is_set = true; + config.output_format = parser.output_format; + break; + } case '?': /* help */ usage5(); break; @@ -1172,16 +1230,16 @@ static int call_getopt(int argc, char **argv) { break; } } - return optind; -} - -static int validate_arguments(int argc) { if (argc - optind > 0) { usage(_("Got unexpected non-option argument")); } - return OK; + process_arguments_wrapper result = { + .config = config, + .error = OK, + }; + return result; } #if defined(__sun__) || defined(__solaris__) || defined(__hpux__) @@ -1300,7 +1358,7 @@ static int dl_bind(int fd, int sap, u_char *addr) { * ***********************************************************************/ -static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { +long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { int fd; u_char mac_addr[25]; @@ -1319,26 +1377,27 @@ static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { #endif /* resolve host name or die (TODO: move this to netutils.c!) */ -static void resolve_host(const char *in, struct in_addr *out) { - struct addrinfo hints, *ai; +void resolve_host(const char *name, struct in_addr *out) { + struct addrinfo hints = { + .ai_family = PF_INET, + }; + struct addrinfo *addr_info; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_INET; - if (getaddrinfo(in, NULL, &hints, &ai) != 0) { + if (getaddrinfo(name, NULL, &hints, &addr_info) != 0) { usage_va(_("Invalid hostname/address - %s"), optarg); } - memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); - freeaddrinfo(ai); + memcpy(out, &((struct sockaddr_in *)addr_info->ai_addr)->sin_addr, sizeof(*out)); + freeaddrinfo(addr_info); } /* parse MAC address string, return 6 bytes (unterminated) or NULL */ -static unsigned char *mac_aton(const char *string) { - static unsigned char result[6]; +unsigned char *mac_aton(const char *string) { + static unsigned char result[MAC_ADDR_LEN]; char tmp[3]; - unsigned i, j; + unsigned byte_counter = 0; - for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { + for (int i = 0; string[i] != '\0' && byte_counter < sizeof(result); i++) { /* ignore ':' and any other non-hex character */ if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) { continue; @@ -1346,27 +1405,25 @@ static unsigned char *mac_aton(const char *string) { tmp[0] = string[i]; tmp[1] = string[i + 1]; tmp[2] = '\0'; - result[j] = strtol(tmp, (char **)NULL, 16); + result[byte_counter] = strtol(tmp, (char **)NULL, 16); i++; - j++; + byte_counter++; } - return (j == 6) ? result : NULL; + return (byte_counter == MAC_ADDR_LEN) ? result : NULL; } -static void print_hardware_address(const unsigned char *address) { - int i; +void print_hardware_address(const unsigned char *address) { printf(_("Hardware address: ")); - for (i = 0; i < 5; i++) { - printf("%2.2x:", address[i]); + for (int addr_idx = 0; addr_idx < MAC_ADDR_LEN; addr_idx++) { + printf("%2.2x:", address[addr_idx]); } - printf("%2.2x", address[i]); putchar('\n'); } /* print usage help */ -static void print_help(void) { +void print_help(void) { print_revision(progname, NP_VERSION); @@ -1382,6 +1439,7 @@ static void print_help(void) { printf(UT_HELP_VRSN); printf(UT_EXTRA_OPTS); + printf(UT_OUTPUT_FORMAT); printf(UT_VERBOSE); printf(" %s\n", "-s, --serverip=IPADDRESS"); @@ -1400,7 +1458,6 @@ static void print_help(void) { printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); printf(UT_SUPPORT); - return; } void print_usage(void) { @@ -1408,6 +1465,4 @@ void print_usage(void) { printf("%s\n", _("Usage:")); printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); printf(" [-i interface] [-m mac]\n"); - - return; } diff --git a/plugins-root/check_dhcp.d/config.h b/plugins-root/check_dhcp.d/config.h new file mode 100644 index 00000000..f189068b --- /dev/null +++ b/plugins-root/check_dhcp.d/config.h @@ -0,0 +1,50 @@ +#pragma once + +#include "../../config.h" +#include "../lib/states.h" +#include +#include +#include "net/if.h" +#include "output.h" + +typedef struct requested_server_struct { + struct in_addr server_address; + bool answered; + struct requested_server_struct *next; +} requested_server; + +typedef struct check_dhcp_config { + bool unicast_mode; /* unicast mode: mimic a DHCP relay */ + bool exclusive_mode; /* exclusive mode aka "rogue DHCP server detection" */ + int num_of_requested_servers; + struct in_addr dhcp_ip; /* server to query (if in unicast mode) */ + struct in_addr requested_address; + bool request_specific_address; + + int dhcpoffer_timeout; + unsigned char *user_specified_mac; + char network_interface_name[IFNAMSIZ]; + requested_server *requested_server_list; + + mp_output_format output_format; + bool output_format_is_set; +} check_dhcp_config; + +check_dhcp_config check_dhcp_config_init(void) { + check_dhcp_config tmp = { + .unicast_mode = false, + .exclusive_mode = false, + .num_of_requested_servers = 0, + .dhcp_ip = {0}, + .requested_address = {0}, + .request_specific_address = false, + + .dhcpoffer_timeout = 2, + .user_specified_mac = NULL, + .network_interface_name = "eth0", + .requested_server_list = NULL, + + .output_format_is_set = false, + }; + return tmp; +} -- cgit v1.2.3-74-g34f1 From d3f22c96bbfa8f31a6d795387f10a825f78c7aef Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 1 Apr 2025 01:18:23 +0200 Subject: check_dhcp: little fix to output --- plugins-root/check_dhcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c index 3732d970..daed9cb0 100644 --- a/plugins-root/check_dhcp.c +++ b/plugins-root/check_dhcp.c @@ -972,7 +972,7 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc /* we didn't receive any DHCPOFFERs */ if (dhcp_offer_list == NULL) { sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL); - xasprintf(&sc_dhcp_results.output, "%s", "No DHCP offers were received"); + xasprintf(&sc_dhcp_results.output, "%s", "No DHCPOFFERs were received"); return sc_dhcp_results; } -- cgit v1.2.3-74-g34f1 From bf0b389381dce2c950834a847db2933196111310 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 1 Apr 2025 01:18:44 +0200 Subject: Adapt tests --- plugins-root/t/check_dhcp.t | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t index ce627736..54949612 100644 --- a/plugins-root/t/check_dhcp.t +++ b/plugins-root/t/check_dhcp.t @@ -12,14 +12,14 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO", "no" ); if ($allow_sudo eq "yes" or $> == 0) { - plan tests => 6; + plan tests => 8; } else { plan skip_all => "Need sudo to test check_dhcp"; } my $sudo = $> == 0 ? '' : 'sudo'; -my $successOutput = '/OK: Received \d+ DHCPOFFER\(s\), \d+ of 1 requested servers responded, max lease time = \d+ sec\./'; -my $failureOutput = '/CRITICAL: (No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; +my $successOutput = '/Received \d+ DHCPOFFER(s)*, max lease time = \d+ seconds/'; +my $failureOutput = '/(No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; my $invalidOutput = '/Invalid hostname/'; my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE", @@ -34,6 +34,8 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID", "An invalid (not known to DNS) hostname", "nosuchhost" ); +my $output_format = "--output-format mp-test-json"; + # try to determince interface my $interface = ''; @@ -49,19 +51,21 @@ my $res; SKIP: { skip('need responsive test host', 2) unless $host_responsive; $res = NPTest->testCmd( - "$sudo ./check_dhcp $interface -u -s $host_responsive" + "$sudo ./check_dhcp $interface -u -s $host_responsive $output_format" ); - is( $res->return_code, 0, "Syntax ok" ); - like( $res->output, $successOutput, "Output OK" ); + is( $res->return_code, 0, "with JSON test format result should always be OK" ); + like( $res->{'mp_test_result'}->{'state'}, "/OK/", "Output OK" ); + like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $successOutput, "Output OK" ); }; SKIP: { skip('need nonresponsive test host', 2) unless $host_nonresponsive; $res = NPTest->testCmd( - "$sudo ./check_dhcp $interface -u -s $host_nonresponsive" + "$sudo ./check_dhcp $interface -u -s $host_nonresponsive $output_format" ); - is( $res->return_code, 2, "Exit code - host nonresponsive" ); - like( $res->output, $failureOutput, "Output OK" ); + is( $res->return_code, 0, "with JSON test format result should always be OK" ); + like( $res->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Exit code - host nonresponsive" ); + like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $failureOutput, "Output OK" ); }; SKIP: { @@ -69,6 +73,6 @@ SKIP: { $res = NPTest->testCmd( "$sudo ./check_dhcp $interface -u -s $hostname_invalid" ); - is( $res->return_code, 3, "Exit code - host invalid" ); + is( $res->return_code, 3, "invalid hostname/address should return UNKNOWN" ); like( $res->output, $invalidOutput, "Output OK" ); }; -- cgit v1.2.3-74-g34f1 From 52338c3423d4b10d2bbeec7a120c7dd2a98fa092 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:33:28 +0200 Subject: check_dhcp: reduce number of tests for weird reasons --- plugins-root/t/check_dhcp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t index 54949612..70392154 100644 --- a/plugins-root/t/check_dhcp.t +++ b/plugins-root/t/check_dhcp.t @@ -12,7 +12,7 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO", "no" ); if ($allow_sudo eq "yes" or $> == 0) { - plan tests => 8; + plan tests => 7; } else { plan skip_all => "Need sudo to test check_dhcp"; } -- cgit v1.2.3-74-g34f1 From 528d2015d0e5c7fcdd7e11029758bafa63086ed2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:02:41 +0200 Subject: Add check_dhcp related files to Makefile --- plugins-root/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am index fffc0575..b6342909 100644 --- a/plugins-root/Makefile.am +++ b/plugins-root/Makefile.am @@ -25,7 +25,8 @@ noinst_PROGRAMS = check_dhcp check_icmp @EXTRAS_ROOT@ EXTRA_PROGRAMS = pst3 EXTRA_DIST = t pst3.c \ - check_icmp.d + check_icmp.d \ + check_dhcp.d BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) -- cgit v1.2.3-74-g34f1 From 612d261cbf467c82f0dcc0ed63e7584d91f194c4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:02:52 +0200 Subject: Revert "check_dhcp: reduce number of tests for weird reasons" This reverts commit 52338c3423d4b10d2bbeec7a120c7dd2a98fa092. --- plugins-root/t/check_dhcp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t index 70392154..54949612 100644 --- a/plugins-root/t/check_dhcp.t +++ b/plugins-root/t/check_dhcp.t @@ -12,7 +12,7 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO", "no" ); if ($allow_sudo eq "yes" or $> == 0) { - plan tests => 7; + plan tests => 8; } else { plan skip_all => "Need sudo to test check_dhcp"; } -- cgit v1.2.3-74-g34f1 From 9f776105d44190587a587b7398f5d907611a20b0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:21:25 +0200 Subject: Reapply "check_dhcp: reduce number of tests for weird reasons" This reverts commit 612d261cbf467c82f0dcc0ed63e7584d91f194c4. --- plugins-root/t/check_dhcp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t index 54949612..70392154 100644 --- a/plugins-root/t/check_dhcp.t +++ b/plugins-root/t/check_dhcp.t @@ -12,7 +12,7 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO", "no" ); if ($allow_sudo eq "yes" or $> == 0) { - plan tests => 8; + plan tests => 7; } else { plan skip_all => "Need sudo to test check_dhcp"; } -- cgit v1.2.3-74-g34f1 From 4ef8af0d9a2acb2e199b613af2d85af86fed24b2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:46:54 +0200 Subject: check_load: remove useless code and do some formatting --- plugins/check_load.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/plugins/check_load.c b/plugins/check_load.c index bf3c89d8..f7e80fdb 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -58,7 +58,6 @@ typedef struct { check_load_config config; } check_load_config_wrapper; static check_load_config_wrapper process_arguments(int argc, char **argv); -static check_load_config_wrapper validate_arguments(check_load_config_wrapper /*config_wrapper*/); void print_help(void); void print_usage(void); @@ -78,9 +77,12 @@ static parsed_thresholds get_threshold(char *arg) { bool valid = false; parsed_thresholds result = { - .load[0] = mp_range_init(), - .load[1] = mp_range_init(), - .load[2] = mp_range_init(), + .load = + { + mp_range_init(), + mp_range_init(), + mp_range_init(), + }, }; size_t arg_length = strlen(arg); @@ -148,10 +150,11 @@ int main(int argc, char **argv) { if (config.take_into_account_cpus && ((numcpus = GET_NUMBER_OF_CPUS()) > 0)) { is_using_scaled_load_values = true; - double scaled_la[3] = {0.0, 0.0, 0.0}; - scaled_la[0] = load_values[0] / numcpus; - scaled_la[1] = load_values[1] / numcpus; - scaled_la[2] = load_values[2] / numcpus; + double scaled_la[3] = { + load_values[0] / numcpus, + load_values[1] / numcpus, + load_values[2] / numcpus, + }; mp_subcheck scaled_load_sc = mp_subcheck_init(); scaled_load_sc = mp_set_subcheck_default_state(scaled_load_sc, STATE_OK); @@ -292,7 +295,6 @@ static check_load_config_wrapper process_arguments(int argc, char **argv) { case output_format_index: { parsed_output_format parser = mp_parse_output_format(optarg); if (!parser.parsing_success) { - // TODO List all available formats here, maybe add anothoer usage function printf("Invalid output format: %s\n", optarg); exit(STATE_UNKNOWN); } @@ -305,8 +307,10 @@ static check_load_config_wrapper process_arguments(int argc, char **argv) { parsed_thresholds warning_range = get_threshold(optarg); result.config.th_load[0].warning = warning_range.load[0]; result.config.th_load[0].warning_is_set = true; + result.config.th_load[1].warning = warning_range.load[1]; result.config.th_load[1].warning_is_set = true; + result.config.th_load[2].warning = warning_range.load[2]; result.config.th_load[2].warning_is_set = true; } break; @@ -314,8 +318,10 @@ static check_load_config_wrapper process_arguments(int argc, char **argv) { parsed_thresholds critical_range = get_threshold(optarg); result.config.th_load[0].critical = critical_range.load[0]; result.config.th_load[0].critical_is_set = true; + result.config.th_load[1].critical = critical_range.load[1]; result.config.th_load[1].critical_is_set = true; + result.config.th_load[2].critical = critical_range.load[2]; result.config.th_load[2].critical_is_set = true; } break; @@ -338,7 +344,7 @@ static check_load_config_wrapper process_arguments(int argc, char **argv) { int index = optind; if (index == argc) { - return validate_arguments(result); + return result; } /* handle the case if both arguments are missing, @@ -347,32 +353,36 @@ static check_load_config_wrapper process_arguments(int argc, char **argv) { parsed_thresholds warning_range = get_threshold(argv[index++]); result.config.th_load[0].warning = warning_range.load[0]; result.config.th_load[0].warning_is_set = true; + result.config.th_load[1].warning = warning_range.load[1]; result.config.th_load[1].warning_is_set = true; + result.config.th_load[2].warning = warning_range.load[2]; result.config.th_load[2].warning_is_set = true; parsed_thresholds critical_range = get_threshold(argv[index++]); result.config.th_load[0].critical = critical_range.load[0]; result.config.th_load[0].critical_is_set = true; + result.config.th_load[1].critical = critical_range.load[1]; result.config.th_load[1].critical_is_set = true; + result.config.th_load[2].critical = critical_range.load[2]; result.config.th_load[2].critical_is_set = true; } else if (index - argc == 1) { parsed_thresholds critical_range = get_threshold(argv[index++]); result.config.th_load[0].critical = critical_range.load[0]; result.config.th_load[0].critical_is_set = true; + result.config.th_load[1].critical = critical_range.load[1]; result.config.th_load[1].critical_is_set = true; + result.config.th_load[2].critical = critical_range.load[2]; result.config.th_load[2].critical_is_set = true; } - return validate_arguments(result); + return result; } -static check_load_config_wrapper validate_arguments(check_load_config_wrapper config_wrapper) { return config_wrapper; } - void print_help(void) { print_revision(progname, NP_VERSION); -- cgit v1.2.3-74-g34f1 From b002a6870b6ef9333d3234cec58b8c2fc1acfeec Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:58:55 +0200 Subject: check_load: Add top x functionality to output --- plugins/check_load.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/check_load.c b/plugins/check_load.c index f7e80fdb..78daa945 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -245,13 +245,15 @@ int main(int argc, char **argv) { mp_subcheck top_proc_sc = mp_subcheck_init(); top_proc_sc = mp_set_subcheck_state(top_proc_sc, STATE_OK); top_processes_result top_proc = print_top_consuming_processes(config.n_procs_to_show); - top_proc_sc.output = ""; + xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes", config.n_procs_to_show); if (top_proc.errorcode == OK) { for (int i = 0; i < config.n_procs_to_show; i++) { xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output, top_proc.top_processes[i]); } } + + mp_add_subcheck_to_check(&overall, top_proc_sc); } mp_exit(overall); -- cgit v1.2.3-74-g34f1 From 351b104894fd52e634d60d79f573be682a2da8d4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 21:59:12 +0200 Subject: check_load some number type fixes --- plugins/check_load.c | 12 ++++++------ plugins/check_load.d/config.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/check_load.c b/plugins/check_load.c index 78daa945..2925bff3 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -65,7 +65,7 @@ typedef struct { int errorcode; char **top_processes; } top_processes_result; -static top_processes_result print_top_consuming_processes(int /*n_procs_to_show*/); +static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show); typedef struct { mp_range load[3]; @@ -248,7 +248,7 @@ int main(int argc, char **argv) { xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes", config.n_procs_to_show); if (top_proc.errorcode == OK) { - for (int i = 0; i < config.n_procs_to_show; i++) { + for (unsigned long i = 0; i < config.n_procs_to_show; i++) { xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output, top_proc.top_processes[i]); } } @@ -337,7 +337,7 @@ static check_load_config_wrapper process_arguments(int argc, char **argv) { print_help(); exit(STATE_UNKNOWN); case 'n': - result.config.n_procs_to_show = atoi(optarg); + result.config.n_procs_to_show = (unsigned long)atol(optarg); break; case '?': /* help */ usage5(); @@ -441,7 +441,7 @@ int cmpstringp(const void *p1, const void *p2) { } #endif /* PS_USES_PROCPCPU */ -static top_processes_result print_top_consuming_processes(int n_procs_to_show) { +static top_processes_result print_top_consuming_processes(unsigned long n_procs_to_show) { top_processes_result result = { .errorcode = OK, }; @@ -462,7 +462,7 @@ static top_processes_result print_top_consuming_processes(int n_procs_to_show) { #ifdef PS_USES_PROCPCPU qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp); #endif /* PS_USES_PROCPCPU */ - int lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) ? (int)chld_out.lines : n_procs_to_show + 1; + unsigned long lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) ? chld_out.lines : n_procs_to_show + 1; result.top_processes = calloc(lines_to_show, sizeof(char *)); if (result.top_processes == NULL) { @@ -471,7 +471,7 @@ static top_processes_result print_top_consuming_processes(int n_procs_to_show) { return result; } - for (int i = 0; i < lines_to_show; i += 1) { + for (unsigned long i = 0; i < lines_to_show; i += 1) { xasprintf(&result.top_processes[i], "%s", chld_out.line[i]); } diff --git a/plugins/check_load.d/config.h b/plugins/check_load.d/config.h index d399c19c..fd735455 100644 --- a/plugins/check_load.d/config.h +++ b/plugins/check_load.d/config.h @@ -6,7 +6,7 @@ typedef struct { mp_thresholds th_load[3]; bool take_into_account_cpus; - int n_procs_to_show; + unsigned long n_procs_to_show; mp_output_format output_format; bool output_format_set; -- cgit v1.2.3-74-g34f1 From bb4ce15997a3023c5c4f2bb434b37699797272da Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 22:47:01 +0200 Subject: Make multiline output look better --- lib/output.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/output.c b/lib/output.c index 61fbf832..fbde490e 100644 --- a/lib/output.c +++ b/lib/output.c @@ -332,7 +332,37 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch mp_subcheck_list *subchecks = NULL; switch (output_format) { - case MP_FORMAT_MULTI_LINE: + case MP_FORMAT_MULTI_LINE: { + char *tmp_string = NULL; + if ((tmp_string = strchr(check.output, '\n')) != NULL) { + // This is a multiline string, put the correct indentation in before proceeding + char *intermediate_string = ""; + bool have_residual_chars = false; + + while (tmp_string != NULL) { + *tmp_string = '\0'; + xasprintf(&intermediate_string, "%s%s\n%s", intermediate_string,check.output, generate_indentation_string(indentation+1)); // one more indentation to make it look better + + if (*(tmp_string + 1) != '\0') { + check.output = tmp_string + 1; + have_residual_chars = true; + } else { + // Null after the \n, so this is the end + have_residual_chars = false; + break; + } + + tmp_string = strchr(check.output, '\n'); + } + + // add the rest (if any) + if (have_residual_chars) { + char *tmp = check.output; + xasprintf(&check.output, "%s\n%s%s", intermediate_string, generate_indentation_string(indentation+1), tmp); + } else { + check.output = intermediate_string; + } + } asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), check.output); @@ -343,6 +373,7 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch subchecks = subchecks->next; } return result; + } default: die(STATE_UNKNOWN, "Invalid format"); } -- cgit v1.2.3-74-g34f1 From b191a8a05543b22bf8e3255d74d358dd42ef1a89 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:16:34 +0200 Subject: check_load: fix tests --- plugins/t/check_load.t | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/t/check_load.t b/plugins/t/check_load.t index 3e453703..fc26bb35 100644 --- a/plugins/t/check_load.t +++ b/plugins/t/check_load.t @@ -33,9 +33,9 @@ cmp_ok( $res->return_code, 'eq', 2, "Load over 0 with per cpu division"); $res = NPTest->testCmd( "./check_load -w 100 -c 100,110" ); cmp_ok( $res->return_code, 'eq', 0, "Plugin can handle non-triplet-arguments"); # like( $res->output, $successOutput, "Output OK"); -like( $res->perf_output, "/load1=$loadValue;~:100.0+;~:100.0+/", "Test handling of non triplet thresholds (load1)"); -like( $res->perf_output, "/load5=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load5)"); -like( $res->perf_output, "/load15=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load15)"); +like( $res->perf_output, "/'load1'=$loadValue;~:100.0+;~:100.0+/", "Test handling of non triplet thresholds (load1)"); +like( $res->perf_output, "/'load5'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load5)"); +like( $res->perf_output, "/'load15'=$loadValue;~:100.0+;~:110.0+/", "Test handling of non triplet thresholds (load15)"); $res = NPTest->testCmd( "./check_load -w 100,100,100 -c 100,100,100 -r" ); -- cgit v1.2.3-74-g34f1 From 45ea3e7b9ffc1486ec89fcf7d702e59b15d414a6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:43:19 +0200 Subject: Fix merge error --- plugins/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 0d043b41..69eab127 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -81,7 +81,7 @@ EXTRA_DIST = t \ check_ntp_time.d \ check_dig.d \ check_cluster.d \ - check_curl.d + check_curl.d \ check_cluster.d \ check_ups.d \ check_fping.d -- cgit v1.2.3-74-g34f1 From e855107eeb882d25ce777562d16886ea9aa2633e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sun, 6 Jul 2025 23:46:16 +0200 Subject: check_curl: clang-format --- plugins/check_curl.c | 771 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 495 insertions(+), 276 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 4806bf14..5b8a06be 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -169,10 +169,13 @@ static void print_help(void); void print_usage(void); static void print_curl_version(void); static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); -static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); +static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); -static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); -static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); +static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, + size_t /*datalen*/); +static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); static curlhelp_ssl_library curlhelp_get_ssl_library(void); static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); @@ -180,9 +183,12 @@ int net_noopenssl_check_certificate(cert_ptr_union *, int, int); static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); -static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); -static int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); +static char *get_header_value(const struct phr_header *headers, size_t nof_headers, + const char *header); +static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +static int get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); @@ -208,14 +214,14 @@ int main(int argc, char **argv) { /* set defaults */ if (config.user_agent == NULL) { - snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, - curl_version()); + snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", + progname, NP_VERSION, VERSION, curl_version()); } if (config.display_html) { printf("", config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, config.virtual_port ? config.virtual_port : config.server_port, - config.server_url); + config.host_name ? config.host_name : config.server_address, + config.virtual_port ? config.virtual_port : config.server_port, config.server_url); } exit(check_http(config)); @@ -313,7 +319,8 @@ static int expected_statuscode(const char *reply, const char *statuscodes) { void handle_curl_option_return_code(CURLcode res, const char *option) { if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } @@ -351,7 +358,8 @@ int lookup_host(const char *host, char *buf, size_t buflen) { inet_ntop(res->ai_family, ptr, addrstr, 100); if (verbose >= 1) { - printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr); + printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, + addrstr); } // Append all IPs to buf as a comma-separated string @@ -416,17 +424,21 @@ mp_state_enum check_http(check_curl_config config) { atexit(cleanup); if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), + "CURLOPT_VERBOSE"); } /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), + "CURLOPT_STDERR"); if (config.automatic_decompression) #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), + "CURLOPT_ACCEPT_ENCODING"); #else - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), + "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ /* initialize buffer for body of the answer */ @@ -434,44 +446,57 @@ mp_state_enum check_http(check_curl_config config) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); } body_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), - "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + (curl_write_callback)curlhelp_buffer_write_callback), + "CURLOPT_WRITEFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), + "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ if (curlhelp_initwritebuffer(&header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); } header_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), - "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, + (curl_write_callback)curlhelp_buffer_write_callback), + "CURLOPT_HEADERFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), + "CURLOPT_WRITEHEADER"); /* set the error buffer */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), + "CURLOPT_ERRORBUFFER"); /* set timeouts */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), "CURLOPT_TIMEOUT"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + "CURLOPT_CONNECTTIMEOUT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), + "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ if (config.haproxy_protocol) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), + "CURLOPT_HAPROXYPROTOCOL"); } - // fill dns resolve cache to make curl connect to the given server_address instead of the host_name, only required for ssl, because we - // use the host_name later on to make SNI happy + // fill dns resolve cache to make curl connect to the given server_address instead of the + // host_name, only required for ssl, because we use the host_name later on to make SNI happy struct curl_slist *host = NULL; char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; if (config.use_ssl && config.host_name != NULL) { CURLcode res; if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), config.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, addrstr); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, + addrstr); host = curl_slist_append(NULL, dnscache); curl_easy_setopt(curl, CURLOPT_RESOLVE, host); if (verbose >= 1) { @@ -486,7 +511,8 @@ mp_state_enum check_http(check_curl_config config) { if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", config.server_address); + snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", + config.server_address); free(config.server_address); config.server_address = new_server_address; } @@ -494,8 +520,9 @@ mp_state_enum check_http(check_curl_config config) { /* compose URL: use the address we want to connect to, set Host: header later */ char url[DEFAULT_BUFFER_SIZE]; snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", config.use_ssl ? "https" : "http", - (config.use_ssl & (config.host_name != NULL)) ? config.host_name : config.server_address, config.server_port, - config.server_url); + (config.use_ssl & (config.host_name != NULL)) ? config.host_name + : config.server_address, + config.server_port, config.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); @@ -503,14 +530,19 @@ mp_state_enum check_http(check_curl_config config) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(config.http_method, "CONNECT") || strstr(config.server_url, "http") == config.server_url) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), "CURLOPT_PROXY"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), "CURLOPT_PROXYPORT"); + if (!strcmp(config.http_method, "CONNECT") || + strstr(config.server_url, "http") == config.server_url) { + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), + "CURLOPT_PROXY"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), + "CURLOPT_PROXYPORT"); if (verbose >= 2) { printf("* curl CURLOPT_PROXY: %s:%d\n", config.server_address, config.server_port); } config.http_method = "GET"; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), "CURLOPT_URL"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), + "CURLOPT_URL"); } /* disable body for HEAD request */ @@ -519,16 +551,21 @@ mp_state_enum check_http(check_curl_config config) { } /* set HTTP protocol version */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + "CURLOPT_HTTP_VERSION"); /* set HTTP method */ if (config.http_method) { if (!strcmp(config.http_method, "POST")) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), + "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), "CURLOPT_CUSTOMREQUEST"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), + "CURLOPT_CUSTOMREQUEST"); } } @@ -545,8 +582,10 @@ mp_state_enum check_http(check_curl_config config) { /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */ char http_header[DEFAULT_BUFFER_SIZE]; if (config.host_name != NULL && force_host_header == NULL) { - if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || (config.virtual_port != HTTPS_PORT && config.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, config.virtual_port); + if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || + (config.virtual_port != HTTPS_PORT && config.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, + config.virtual_port); } else { snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", config.host_name); } @@ -563,43 +602,53 @@ mp_state_enum check_http(check_curl_config config) { for (int i = 0; i < config.http_opt_headers_count; i++) { header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } - /* This cannot be free'd here because a redirection will then try to access this and segfault */ + /* This cannot be free'd here because a redirection will then try to access this and + * segfault */ /* Covered in a testcase in tests/check_http.t */ /* free(http_opt_headers); */ } /* set HTTP headers */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), + "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ if (config.use_ssl) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ if (config.client_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), "CURLOPT_SSLCERT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), + "CURLOPT_SSLCERT"); } if (config.client_privkey) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); } if (config.ca_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), + "CURLOPT_CAINFO"); } if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), + "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks * TODO: depending on more options have aspects we want * to be less tolerant about ssl verfications */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), + "CURLOPT_SSL_VERIFYHOST"); } /* detect SSL library used by libcurl */ @@ -621,15 +670,19 @@ mp_state_enum check_http(check_curl_config config) { # endif /* USE_OPENSSL */ /* libcurl is built with OpenSSL, monitoring plugins, so falling * back to manually extracting certificate information */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), + "CURLOPT_CERTINFO"); break; case CURLHELP_SSL_LIBRARY_NSS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) /* NSS: support for CERTINFO is implemented since 7.34.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), + "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", curlhelp_get_ssl_library_string(ssl_library)); # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ break; @@ -637,50 +690,63 @@ mp_state_enum check_http(check_curl_config config) { case CURLHELP_SSL_LIBRARY_GNUTLS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), + "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", curlhelp_get_ssl_library_string(ssl_library)); # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ break; case CURLHELP_SSL_LIBRARY_UNKNOWN: default: - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must " + "implement first)\n", curlhelp_get_ssl_library_string(ssl_library)); break; } # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ - if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { + if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || + ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { add_sslctx_verify_fun = true; } else { - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " + die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no " + "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " "too old and has no CURLOPT_CERTINFO)\n"); } # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ } -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ +# if LIBCURL_VERSION_NUM >= \ + MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + "CURLOPT_SSL_CTX_FUNCTION"); } # endif #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), "CURLOPT_USERAGENT"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), + "CURLOPT_USERAGENT"); /* proxy-authentication */ if (strcmp(config.proxy_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + "CURLOPT_PROXYUSERPWD"); } /* authentication */ if (strcmp(config.user_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), "CURLOPT_USERPWD"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), + "CURLOPT_USERPWD"); } /* TODO: parameter auth method, bitfield of following methods: @@ -695,26 +761,32 @@ mp_state_enum check_http(check_curl_config config) { * CURLAUTH_ANYSAFE: most secure, without BASIC * or CURLAUTH_ANY: most secure, even BASIC if necessary * - * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); + * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, + * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); */ /* handle redirections */ if (config.onredirect == STATE_DEPENDENT) { if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); + handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), + "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), "CURLOPT_MAXREDIRS"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), + "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), - "CURLOPT_REDIR_PROTOCOLS_STR"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + "CURLOPT_REDIR_PROTOCOLS_STR"); #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), - "CURLOPT_REDIRECT_PROTOCOLS"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), + "CURLOPT_REDIRECT_PROTOCOLS"); #endif /* TODO: handle the following aspects of redirection, make them @@ -722,7 +794,8 @@ mp_state_enum check_http(check_curl_config config) { CURLOPT_POSTREDIR: method switch CURLINFO_REDIRECT_URL: custom redirect option CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols - CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size? + CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option + here is nice like for expected page size? */ } else { /* old style redirection is handled below */ @@ -736,8 +809,9 @@ mp_state_enum check_http(check_curl_config config) { /* IPv4 or IPv6 forced DNS resolution */ if (address_family == AF_UNSPEC) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); } else if (address_family == AF_INET) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); @@ -753,7 +827,8 @@ mp_state_enum check_http(check_curl_config config) { if (!strcmp(config.http_method, "POST") || !strcmp(config.http_method, "PUT")) { /* set content of payload for POST and PUT */ if (config.http_content_type) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", config.http_content_type); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", + config.http_content_type); header_list = curl_slist_append(header_list, http_header); } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string @@ -763,27 +838,42 @@ mp_state_enum check_http(check_curl_config config) { } if (!strcmp(config.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), "CURLOPT_POSTFIELDS"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), + "CURLOPT_POSTFIELDS"); } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), - "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, strlen(config.http_post_data)) < 0) { - die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_READFUNCTION, + (curl_read_callback)curlhelp_buffer_read_callback), + "CURLOPT_READFUNCTION"); + if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, + strlen(config.http_post_data)) < 0) { + die(STATE_UNKNOWN, + "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } put_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)strlen(config.http_post_data)), - "CURLOPT_INFILESIZE"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(config.http_post_data)), + "CURLOPT_INFILESIZE"); } } /* cookie handling */ if (config.cookie_jar_file != NULL) { - /* enable reading cookies from a file, and if the filename is an empty string, only enable the curl cookie engine */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); - /* now enable saving cookies to a file, but only if the filename is not an empty string, since writing it would fail */ + /* enable reading cookies from a file, and if the filename is an empty string, only enable + * the curl cookie engine */ + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + "CURLOPT_COOKIEFILE"); + /* now enable saving cookies to a file, but only if the filename is not an empty string, + * since writing it would fail */ if (*config.cookie_jar_file) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); + handle_curl_option_return_code( + curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + "CURLOPT_COOKIEJAR"); } } @@ -806,7 +896,8 @@ mp_state_enum check_http(check_curl_config config) { /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), config.server_port, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } @@ -821,13 +912,14 @@ mp_state_enum check_http(check_curl_config config) { /* check certificate with OpenSSL functions, curl has been built against OpenSSL * and we actually have OpenSSL in the monitoring tools */ - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, + config.days_till_exp_crit); if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n"); + die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " + "callback used and not linked against OpenSSL\n"); # endif /* USE_OPENSSL */ } else { struct curl_slist *slist; @@ -841,7 +933,8 @@ mp_state_enum check_http(check_curl_config config) { */ const char *raw_cert = NULL; for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { - for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { + for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; + slist = slist->next) { if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); } @@ -854,32 +947,38 @@ mp_state_enum check_http(check_curl_config config) { GOT_FIRST_CERT: if (!raw_cert) { snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Cannot retrieve certificates from CERTINFO information - certificate data was empty")); + _("Cannot retrieve certificates from CERTINFO information - " + "certificate data was empty")); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO *cert_BIO = BIO_new(BIO_s_mem()); BIO_write(cert_BIO, raw_cert, strlen(raw_cert)); cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); if (!cert) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error")); + snprintf( + msg, DEFAULT_BUFFER_SIZE, + _("Cannot read certificate from CERTINFO information - BIO error")); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO_free(cert_BIO); - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, config.days_till_exp_crit); + result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, + config.days_till_exp_crit); if (!config.continue_after_check_cert) { return result_ssl; } # else /* USE_OPENSSL */ - /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal, - * so we use the libcurl CURLINFO data + /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our + * disposal, so we use the libcurl CURLINFO data */ - result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit); + result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, + days_till_exp_crit); if (!continue_after_check_cert) { return result_ssl; } # endif /* USE_OPENSSL */ } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), res, + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Cannot retrieve certificates - cURL returned %d - %s"), res, curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } @@ -892,28 +991,40 @@ mp_state_enum check_http(check_curl_config config) { * performance data to the answer always */ double total_time; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); + handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), + "CURLINFO_TOTAL_TIME"); int page_len = get_content_length(&header_buf, &body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); double time_appconnect; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), + "CURLINFO_APPCONNECT_TIME"); double time_headers; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + "CURLINFO_PRETRANSFER_TIME"); double time_firstbyte; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), - "CURLINFO_STARTTRANSFER_TIME"); - - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time, config.thlds, config.socket_timeout), - perfd_size(page_len, config.min_page_len), perfd_time_connect(time_connect, config.socket_timeout), - config.use_ssl ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) : "", + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), + "CURLINFO_STARTTRANSFER_TIME"); + + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", + perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_size(page_len, config.min_page_len), + perfd_time_connect(time_connect, config.socket_timeout), + config.use_ssl + ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) + : "", perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), perfd_time_firstbyte(time_firstbyte - time_headers, config.socket_timeout), perfd_time_transfer(total_time - time_firstbyte, config.socket_timeout)); } else { - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", perfd_time(total_time, config.thlds, config.socket_timeout), + snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", + perfd_time(total_time, config.thlds, config.socket_timeout), perfd_size(page_len, config.min_page_len)); } @@ -924,37 +1035,45 @@ mp_state_enum check_http(check_curl_config config) { /* get status line of answer, check sanity of HTTP code */ if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); + snprintf(msg, DEFAULT_BUFFER_SIZE, + "Unparsable status line in %.3g seconds response time|%s\n", total_time, + perfstring); /* we cannot know the major/minor version here for sure as we cannot parse the first line */ die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg); } status_line_initialized = true; /* get result code from cURL */ - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE"); + handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), + "CURLINFO_RESPONSE_CODE"); if (verbose >= 2) { printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); } /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, (config.no_body ? " [[ skipped ]]" : body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, + (config.no_body ? " [[ skipped ]]" : body_buf.buf)); } /* make sure the status line matches the response we are looking for */ if (!expected_statuscode(status_line.first_line, config.server_expect)) { if (config.server_port == HTTP_PORT) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); - } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), config.server_port, + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line); + } else { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Invalid HTTP response received from host on port %d: %s\n"), + config.server_port, status_line.first_line); } - die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", config.show_body ? body_buf.buf : ""); + die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", + config.show_body ? body_buf.buf : ""); } int result = STATE_OK; if (config.server_expect_yn) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); + snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), + config.server_expect); if (verbose) { printf("%s\n", msg); } @@ -962,7 +1081,8 @@ mp_state_enum check_http(check_curl_config config) { } else { /* illegal return codes result in a critical state */ if (code >= 600 || code < 100) { - die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg); + die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), + status_line.http_code, status_line.msg); /* server errors result in a critical state */ } else if (code >= 500) { result = STATE_CRITICAL; @@ -995,12 +1115,15 @@ mp_state_enum check_http(check_curl_config config) { /* libcurl redirection internally, handle error states here */ if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); + handle_curl_option_return_code( + curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); } if (redir_depth > config.max_depth) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", config.max_depth); + snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", + config.max_depth); die(STATE_WARNING, "HTTP WARNING - %s", msg); } } @@ -1008,7 +1131,8 @@ mp_state_enum check_http(check_curl_config config) { /* check status codes, set exit status accordingly */ if (status_line.http_code != code) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, code); + string_statuscode(status_line.http_major, status_line.http_minor), + status_line.http_code, status_line.msg, code); } if (config.maximum_age >= 0) { @@ -1029,9 +1153,10 @@ mp_state_enum check_http(check_curl_config config) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, - config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, - config.server_url); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), + msg, output_header_search, config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, + config.server_port, config.server_url); strcpy(msg, tmp); @@ -1051,9 +1176,10 @@ mp_state_enum check_http(check_curl_config config) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, - config.use_ssl ? "https" : "http", config.host_name ? config.host_name : config.server_address, config.server_port, - config.server_url); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), + msg, output_string_search, config.use_ssl ? "https" : "http", + config.host_name ? config.host_name : config.server_address, + config.server_port, config.server_url); strcpy(msg, tmp); @@ -1065,10 +1191,12 @@ mp_state_enum check_http(check_curl_config config) { regex_t preg; regmatch_t pmatch[REGS]; int errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); - if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { + if ((errcode == 0 && !config.invert_regex) || + (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ result = max_state_alt(STATE_OK, result); - } else if ((errcode == REG_NOMATCH && !config.invert_regex) || (errcode == 0 && config.invert_regex)) { + } else if ((errcode == REG_NOMATCH && !config.invert_regex) || + (errcode == 0 && config.invert_regex)) { if (!config.invert_regex) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -1123,10 +1251,13 @@ mp_state_enum check_http(check_curl_config config) { } } - /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */ - die(max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", state_text(result), - string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, - strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, + /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), + * msg); */ + die(max_state_alt(result, result_ssl), + "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", + state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), + status_line.http_code, status_line.msg, strlen(msg) > 0 ? " - " : "", msg, page_len, + total_time, (config.display_html ? "" : ""), perfstring, (config.show_body ? body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); @@ -1157,8 +1288,9 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { struct phr_header headers[255]; size_t msglen; size_t nof_headers = 255; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, - &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); if (res == -1) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); @@ -1171,8 +1303,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { } if (++redir_depth > config.max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), config.max_depth, location, - (config.display_html ? "" : "")); + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), + config.max_depth, location, (config.display_html ? "" : "")); } UriParserStateA state; @@ -1180,8 +1312,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { state.uri = &uri; if (uriParseUriA(&state, location) != URI_SUCCESS) { if (state.errorCode == URI_ERROR_SYNTAX) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), location, - (config.display_html ? "" : "")); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), + location, (config.display_html ? "" : "")); } else if (state.errorCode == URI_ERROR_MALLOC) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); } @@ -1234,8 +1366,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { } } if (new_port > MAX_PORT) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, location, - config.display_html ? "" : ""); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, + location, config.display_html ? "" : ""); } /* by RFC 7231 relative URLs in Location should be taken relative to @@ -1259,14 +1391,19 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { const UriPathSegmentA *p = uri.pathHead; for (; p; p = p->next) { strncat(new_url, "/", DEFAULT_BUFFER_SIZE); - strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); + strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), + DEFAULT_BUFFER_SIZE - 1); } } - if (config.server_port == new_port && !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(config.server_url, new_url)) { - die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), config.use_ssl ? "https" : "http", - new_host, new_port, new_url, (config.display_html ? "" : "")); + if (config.server_port == new_port && + !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && + (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && + !strcmp(config.server_url, new_url)) { + die(STATE_CRITICAL, + _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), + config.use_ssl ? "https" : "http", new_host, new_port, new_url, + (config.display_html ? "" : "")); } /* set new values for redirected request */ @@ -1293,7 +1430,8 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { if (verbose) { printf(_("Redirection to %s://%s:%d%s\n"), config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, config.server_port, config.server_url); + config.host_name ? config.host_name : config.server_address, config.server_port, + config.server_url); } /* TODO: the hash component MUST be taken from the original URL and @@ -1326,50 +1464,51 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { STATE_REGEX }; - static struct option longopts[] = {STD_LONG_OPTS, - {"link", no_argument, 0, 'L'}, - {"nohtml", no_argument, 0, 'n'}, - {"ssl", optional_argument, 0, 'S'}, - {"sni", no_argument, 0, SNI_OPTION}, - {"post", required_argument, 0, 'P'}, - {"method", required_argument, 0, 'j'}, - {"IP-address", required_argument, 0, 'I'}, - {"url", required_argument, 0, 'u'}, - {"port", required_argument, 0, 'p'}, - {"authorization", required_argument, 0, 'a'}, - {"proxy-authorization", required_argument, 0, 'b'}, - {"header-string", required_argument, 0, 'd'}, - {"string", required_argument, 0, 's'}, - {"expect", required_argument, 0, 'e'}, - {"regex", required_argument, 0, 'r'}, - {"ereg", required_argument, 0, 'r'}, - {"eregi", required_argument, 0, 'R'}, - {"linespan", no_argument, 0, 'l'}, - {"onredirect", required_argument, 0, 'f'}, - {"certificate", required_argument, 0, 'C'}, - {"client-cert", required_argument, 0, 'J'}, - {"private-key", required_argument, 0, 'K'}, - {"ca-cert", required_argument, 0, CA_CERT_OPTION}, - {"verify-cert", no_argument, 0, 'D'}, - {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, - {"useragent", required_argument, 0, 'A'}, - {"header", required_argument, 0, 'k'}, - {"no-body", no_argument, 0, 'N'}, - {"max-age", required_argument, 0, 'M'}, - {"content-type", required_argument, 0, 'T'}, - {"pagesize", required_argument, 0, 'm'}, - {"invert-regex", no_argument, NULL, INVERT_REGEX}, - {"state-regex", required_argument, 0, STATE_REGEX}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"extended-perfdata", no_argument, 0, 'E'}, - {"show-body", no_argument, 0, 'B'}, - {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, - {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, - {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, - {"cookie-jar", required_argument, 0, COOKIE_JAR}, - {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, - {0, 0, 0, 0}}; + static struct option longopts[] = { + STD_LONG_OPTS, + {"link", no_argument, 0, 'L'}, + {"nohtml", no_argument, 0, 'n'}, + {"ssl", optional_argument, 0, 'S'}, + {"sni", no_argument, 0, SNI_OPTION}, + {"post", required_argument, 0, 'P'}, + {"method", required_argument, 0, 'j'}, + {"IP-address", required_argument, 0, 'I'}, + {"url", required_argument, 0, 'u'}, + {"port", required_argument, 0, 'p'}, + {"authorization", required_argument, 0, 'a'}, + {"proxy-authorization", required_argument, 0, 'b'}, + {"header-string", required_argument, 0, 'd'}, + {"string", required_argument, 0, 's'}, + {"expect", required_argument, 0, 'e'}, + {"regex", required_argument, 0, 'r'}, + {"ereg", required_argument, 0, 'r'}, + {"eregi", required_argument, 0, 'R'}, + {"linespan", no_argument, 0, 'l'}, + {"onredirect", required_argument, 0, 'f'}, + {"certificate", required_argument, 0, 'C'}, + {"client-cert", required_argument, 0, 'J'}, + {"private-key", required_argument, 0, 'K'}, + {"ca-cert", required_argument, 0, CA_CERT_OPTION}, + {"verify-cert", no_argument, 0, 'D'}, + {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, + {"useragent", required_argument, 0, 'A'}, + {"header", required_argument, 0, 'k'}, + {"no-body", no_argument, 0, 'N'}, + {"max-age", required_argument, 0, 'M'}, + {"content-type", required_argument, 0, 'T'}, + {"pagesize", required_argument, 0, 'm'}, + {"invert-regex", no_argument, NULL, INVERT_REGEX}, + {"state-regex", required_argument, 0, STATE_REGEX}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"extended-perfdata", no_argument, 0, 'E'}, + {"show-body", no_argument, 0, 'B'}, + {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, + {"http-version", required_argument, 0, HTTP_VERSION_OPTION}, + {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, + {"cookie-jar", required_argument, 0, COOKIE_JAR}, + {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, + {0, 0, 0, 0}}; check_curl_config_wrapper result = { .errorcode = OK, @@ -1407,7 +1546,9 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { bool specify_port = false; while (true) { - int option_index = getopt_long(argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", longopts, &option); + int option_index = getopt_long( + argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:DnlLS::m:M:NEB", + longopts, &option); if (option_index == -1 || option_index == EOF || option_index == 1) { break; } @@ -1450,7 +1591,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { free(result.config.host_name); result.config.host_name = strndup(optarg, host_name_length); } - } else if ((p = strchr(result.config.host_name, ':')) != NULL && strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ + } else if ((p = strchr(result.config.host_name, ':')) != NULL && + strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ result.config.virtual_port = atoi(p); /* cut off the port */ host_name_length = strlen(result.config.host_name) - strlen(p) - 1; @@ -1503,10 +1645,12 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; case 'k': /* Additional headers */ if (result.config.http_opt_headers_count == 0) { - result.config.http_opt_headers = malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); + result.config.http_opt_headers = + malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); } else { result.config.http_opt_headers = - realloc(result.config.http_opt_headers, sizeof(char *) * (++result.config.http_opt_headers_count)); + realloc(result.config.http_opt_headers, + sizeof(char *) * (++result.config.http_opt_headers_count)); } result.config.http_opt_headers[result.config.http_opt_headers_count - 1] = optarg; break; @@ -1615,7 +1759,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ else { - usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 (with optional '+' suffix)")); + usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " + "(with optional '+' suffix)")); } } # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) @@ -1681,21 +1826,26 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } else if (!strcmp(optarg, "follow")) { result.config.onredirect = STATE_DEPENDENT; } else if (!strcmp(optarg, "stickyport")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST | STICKY_PORT; } else if (!strcmp(optarg, "sticky")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST; } else if (!strcmp(optarg, "follow")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_HTTP_CURL, + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_NONE; } else if (!strcmp(optarg, "curl")) { - result.config.onredirect = STATE_DEPENDENT, result.config.followmethod = FOLLOW_LIBCURL; + result.config.onredirect = STATE_DEPENDENT, + result.config.followmethod = FOLLOW_LIBCURL; } else { usage2(_("Invalid onredirect option"), optarg); } if (verbose >= 2) { - printf(_("* Following redirects set to %s\n"), state_text(result.config.onredirect)); + printf(_("* Following redirects set to %s\n"), + state_text(result.config.onredirect)); } break; case 'd': /* string or substring */ @@ -1791,7 +1941,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.maximum_age = atoi(optarg) * 60 * 60; } else if (option_length && optarg[option_length - 1] == 'd') { result.config.maximum_age = atoi(optarg) * 60 * 60 * 24; - } else if (option_length && (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) { + } else if (option_length && + (optarg[option_length - 1] == 's' || isdigit(optarg[option_length - 1]))) { result.config.maximum_age = atoi(optarg); } else { fprintf(stderr, "unparsable max-age: %s\n", optarg); @@ -1860,7 +2011,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); - if (critical_thresholds && result.config.thlds->critical->end > (double)result.config.socket_timeout) { + if (critical_thresholds && + result.config.thlds->critical->end > (double)result.config.socket_timeout) { result.config.socket_timeout = (int)result.config.thlds->critical->end + 1; } if (verbose >= 2) { @@ -1890,32 +2042,39 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", thlds->warning, thlds->warning ? thlds->warning->end : 0, thlds->critical, + return fperfdata("time", elapsed_time, "s", thlds->warning, + thlds->warning ? thlds->warning->end : 0, thlds->critical, thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); } char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { - return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { - return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { - return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { - return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, + true, socket_timeout); } char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { - return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); + return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, + true, socket_timeout); } char *perfd_size(int page_len, int min_page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, true, 0, false, 0); + return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, + true, 0, false, 0); } void print_help(void) { @@ -1929,7 +2088,8 @@ void print_help(void) { printf("%s\n", _("strings and regular expressions, check connection times, and report on")); printf("%s\n", _("certificate expiration times.")); printf("\n"); - printf("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); + printf("%s\n", + _("It makes use of libcurl to do so. It tries to be as compatible to check_http")); printf("%s\n", _("as possible.")); printf("\n\n"); @@ -1947,7 +2107,8 @@ void print_help(void) { printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); printf(" %s\n", "-I, --IP-address=ADDRESS"); - printf(" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); + printf(" %s\n", + _("IP address or name (use numeric address if possible to bypass DNS lookup).")); printf(" %s\n", "-p, --port=INTEGER"); printf(" %s", _("Port number (default: ")); printf("%d)\n", HTTP_PORT); @@ -1956,27 +2117,37 @@ void print_help(void) { #ifdef LIBCURL_FEATURE_SSL printf(" %s\n", "-S, --ssl=VERSION[+]"); - printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); + printf(" %s\n", + _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); - printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); - printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually disabled in libcurl")); + printf( + " %s\n", + _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); + printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually " + "disabled in libcurl")); printf(" %s\n", "--sni"); printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); # if LIBCURL_VERSION_NUM >= 0x071801 - printf(" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); + printf(" %s\n", + _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and")); printf(" %s\n", _(" SNI only really works since TLSv1.0")); # else printf(" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1")); # endif printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); - printf(" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); - printf(" %s\n", _("A STATE_WARNING is returned if the certificate has a validity less than the")); - printf(" %s\n", _("first agument's value. If there is a second argument and the certificate's")); + printf(" %s\n", + _("Minimum number of days a certificate has to be valid. Port defaults to 443.")); + printf(" %s\n", + _("A STATE_WARNING is returned if the certificate has a validity less than the")); + printf(" %s\n", + _("first agument's value. If there is a second argument and the certificate's")); printf(" %s\n", _("validity is less than its value, a STATE_CRITICAL is returned.")); - printf(" %s\n", _("(When this option is used the URL is not checked by default. You can use")); + printf(" %s\n", + _("(When this option is used the URL is not checked by default. You can use")); printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); printf(" %s\n", "--continue-after-certificate"); - printf(" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); + printf(" %s\n", + _("Allows the HTTP check to continue after performing the certificate check.")); printf(" %s\n", _("Does nothing unless -C is used.")); printf(" %s\n", "-J, --client-cert=FILE"); printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); @@ -1994,7 +2165,8 @@ void print_help(void) { printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); printf(" %s", _("the first (status) line of the server response (default: ")); printf("%s)\n", HTTP_EXPECT); - printf(" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); + printf(" %s\n", + _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); printf(" %s\n", "-d, --header-string=STRING"); printf(" %s\n", _("String to expect in the response headers")); printf(" %s\n", "-s, --string=STRING"); @@ -2003,7 +2175,8 @@ void print_help(void) { printf(" %s\n", _("URL to GET or POST (default: /)")); printf(" %s\n", "-P, --post=STRING"); printf(" %s\n", _("URL decoded http POST data")); - printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); + printf(" %s\n", + "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)"); printf(" %s\n", _("Set HTTP method.")); printf(" %s\n", "-N, --no-body"); printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); @@ -2023,7 +2196,8 @@ void print_help(void) { printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); printf(" %s\n", _("can be changed with --state--regex)")); printf(" %s\n", "--state-regex=STATE"); - printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of \"critical\",\"warning\"")); + printf(" %s\n", _("Return STATE if regex is found, OK if not. STATE can be one of " + "\"critical\",\"warning\"")); printf(" %s\n", "-a, --authorization=AUTH_PAIR"); printf(" %s\n", _("Username:password on sites with basic authentication")); printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); @@ -2031,7 +2205,9 @@ void print_help(void) { printf(" %s\n", "-A, --useragent=STRING"); printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); printf(" %s\n", "-k, --header=STRING"); - printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); + printf( + " %s\n", + _("Any other tags to be sent in http header. Use multiple times for additional headers")); printf(" %s\n", "-E, --extended-perfdata"); printf(" %s\n", _("Print additional performance data")); printf(" %s\n", "-B, --show-body"); @@ -2047,20 +2223,25 @@ void print_help(void) { printf(" %s", _("Maximal number of redirects (default: ")); printf("%d)\n", DEFAULT_MAX_REDIRS); printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); - printf(" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); + printf(" %s\n", + _("Minimum page size required (bytes) : Maximum page size required (bytes)")); printf("\n"); printf(" %s\n", "--http-version=VERSION"); printf(" %s\n", _("Connect via specific HTTP protocol.")); - printf(" %s\n", _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); + printf(" %s\n", + _("1.0 = HTTP/1.0, 1.1 = HTTP/1.1, 2.0 = HTTP/2 (HTTP/2 will fail without -S)")); printf(" %s\n", "--enable-automatic-decompression"); printf(" %s\n", _("Enable automatic decompression of body (CURLOPT_ACCEPT_ENCODING).")); printf(" %s\n", "--haproxy-protocol"); printf(" %s\n", _("Send HAProxy proxy protocol v1 header (CURLOPT_HAPROXYPROTOCOL).")); printf(" %s\n", "--cookie-jar=FILE"); printf(" %s\n", _("Store cookies in the cookie jar and send them out when requested.")); - printf(" %s\n", _("Specify an empty string as FILE to enable curl's cookie engine without saving")); - printf(" %s\n", _("the cookies to disk. Only enabling the engine without saving to disk requires")); - printf(" %s\n", _("handling multiple requests internally to curl, so use it with --onredirect=curl")); + printf(" %s\n", + _("Specify an empty string as FILE to enable curl's cookie engine without saving")); + printf(" %s\n", + _("the cookies to disk. Only enabling the engine without saving to disk requires")); + printf(" %s\n", + _("handling multiple requests internally to curl, so use it with --onredirect=curl")); printf("\n"); printf(UT_WARN_CRIT); @@ -2072,10 +2253,13 @@ void print_help(void) { printf("\n"); printf("%s\n", _("Notes:")); printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); - printf(" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); - printf(" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); + printf(" %s\n", + _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); + printf(" %s\n", + _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); - printf(" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); + printf(" %s\n", + _("checking a virtual server that uses 'host headers' you must supply the FQDN")); printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); #ifdef LIBCURL_FEATURE_SSL @@ -2091,38 +2275,53 @@ void print_help(void) { printf("%s\n", _("Examples:")); printf(" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com"); printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); - printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); - printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); + printf(" %s\n", + _("a STATE_OK will be returned. When the server returns its content but exceeds")); + printf(" %s\n", + _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); printf(" %s\n", _("a STATE_CRITICAL will be returned.")); printf("\n"); printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14"); - printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); - printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); - printf(" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); + printf(" %s\n", + _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); + printf(" %s\n", + _("a STATE_OK is returned. When the certificate is still valid, but for less than")); + printf(" %s\n", + _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); printf(" %s\n\n", _("the certificate is expired.")); printf("\n"); printf(" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14"); - printf(" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); - printf(" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); + printf(" %s\n", + _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); + printf(" %s\n", + _("a STATE_OK is returned. When the certificate is still valid, but for less than")); printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); - printf(" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); + printf(" %s\n", + _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); #endif printf("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:"); printf(" %s\n", _("It is recommended to use an environment proxy like:")); - printf(" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); + printf(" %s\n", + _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org")); printf(" %s\n", _("legacy proxy requests in check_http style still work:")); - printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org")); + printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ " + "-H www.monitoring-plugins.org")); #ifdef LIBCURL_FEATURE_SSL printf("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); printf(" %s\n", _("It is recommended to use an environment proxy like:")); - printf(" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); + printf(" %s\n", + _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S")); printf(" %s\n", _("legacy proxy requests in check_http style still work:")); - printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); - printf(" %s\n", _("all these options are needed: -I -p -u -S(sl) -j CONNECT -H ")); - printf(" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); - printf(" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); + printf(" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j " + "CONNECT -H www.verisign.com ")); + printf(" %s\n", _("all these options are needed: -I -p -u " + "-S(sl) -j CONNECT -H ")); + printf(" %s\n", + _("a STATE_OK will be returned. When the server returns its content but exceeds")); + printf(" %s\n", + _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); printf(" %s\n", _("a STATE_CRITICAL will be returned.")); #endif @@ -2133,10 +2332,12 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); printf(" %s -H | -I [-u ] [-p ]\n", progname); - printf(" [-J ] [-K ] [--ca-cert ] [-D]\n"); + printf(" [-J ] [-K ] [--ca-cert ] [-D]\n"); printf(" [-w ] [-c ] [-t ] [-L] [-E] [-a auth]\n"); printf(" [-b proxy_auth] [-f ]\n"); - printf(" [-e ] [-d string] [-s string] [-l] [-r | -R ]\n"); + printf(" [-e ] [-d string] [-s string] [-l] [-r | -R ]\n"); printf(" [-P string] [-m :] [-4|-6] [-N] [-M ]\n"); printf(" [-A string] [-k string] [-S ] [--sni] [--haproxy-protocol]\n"); printf(" [-T ] [-j method]\n"); @@ -2356,22 +2557,26 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } -char *get_header_value(const struct phr_header *headers, const size_t nof_headers, const char *header) { +char *get_header_value(const struct phr_header *headers, const size_t nof_headers, + const char *header) { for (size_t i = 0; i < nof_headers; i++) { - if (headers[i].name != NULL && strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { + if (headers[i].name != NULL && + strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { return strndup(headers[i].value, headers[i].value_len); } } return NULL; } -int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { +int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], + int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; curlhelp_statusline status_line; size_t msglen; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, - &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); if (res == -1) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); @@ -2401,26 +2606,30 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D time_t srv_data = curl_getdate(server_date, NULL); time_t doc_data = curl_getdate(document_date, NULL); if (verbose >= 2) { - printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); + printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, + document_date, (int)doc_data); } if (srv_data <= 0) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, + server_date); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else if (doc_data <= 0) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, + document_date); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else if (doc_data > srv_data + 30) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, + (int)doc_data - (int)srv_data); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); @@ -2429,14 +2638,16 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D if (n > (60 * 60 * 24 * 2)) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float)n) / (60 * 60 * 24)); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, + ((float)n) / (60 * 60 * 24)); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, + n / (60 * 60), (n / 60) % 60, n % 60); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); @@ -2454,13 +2665,15 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D return date_result; } -int get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf) { +int get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf) { struct phr_header headers[255]; size_t nof_headers = 255; size_t msglen; curlhelp_statusline status_line; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, &status_line.http_minor, - &status_line.http_code, &status_line.msg, &msglen, headers, &nof_headers, 0); + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); if (res == -1) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); @@ -2514,7 +2727,8 @@ curlhelp_ssl_library curlhelp_get_ssl_library(void) { } if (verbose >= 2) { - printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); + printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, + ssl_library); } free(ssl_version); @@ -2560,7 +2774,8 @@ time_t parse_cert_date(const char *s) { /* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to * OpenSSL could be this function */ -int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { +int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, + int days_till_exp_crit) { struct curl_slist *slist; int cname_found = 0; char *start_date_str = NULL; @@ -2622,14 +2837,16 @@ HAVE_FIRST_CERT: start_date = parse_cert_date(start_date_str); if (start_date <= 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); puts(msg); return STATE_WARNING; } end_date = parse_cert_date(end_date_str); if (end_date <= 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); puts(msg); return STATE_WARNING; } @@ -2648,8 +2865,9 @@ HAVE_FIRST_CERT: tzset(); if (days_left > 0 && days_left <= days_till_exp_warn) { - printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", - host_name, days_left, timestamp); + printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left, + timestamp); if (days_left > days_till_exp_crit) { status = STATE_WARNING; } else { @@ -2662,8 +2880,9 @@ HAVE_FIRST_CERT: time_remaining = (int)time_left / 60; } - printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, - time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); + printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining, + time_left >= 3600 ? "hours" : "minutes", timestamp); if (days_left > days_till_exp_crit) { status = STATE_WARNING; @@ -2674,8 +2893,8 @@ HAVE_FIRST_CERT: printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp); status = STATE_CRITICAL; } else if (days_left == 0) { - printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, - timestamp); + printf(_("%s - Certificate '%s' just expired (%s).\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, timestamp); if (days_left > days_till_exp_crit) { status = STATE_WARNING; } else { -- cgit v1.2.3-74-g34f1 From 661ecff45c5f4c41c22ef9fd4fe308100b97d6bf Mon Sep 17 00:00:00 2001 From: Richard Laager Date: Fri, 11 Jul 2025 18:19:31 -0500 Subject: check_ssh: Fix buffer overflow A buffer overflow was occurring when the server responded with: Exceeded MaxStartups\r\n glibc would then abort() with the following output: *** buffer overflow detected ***: terminated It was the memset() that was overflowing the buffer. But the memmove() needed fixing too. First off, there was an off-by-one error in both the memmove() and memset(). byte_offset was already set to the start of the data _past_ the newline (i.e. len + 1). For the memmove(), incrementing that by 1 again lost the first character of the additional output. For the memset(), this causes a buffer overflow. Second, the memset() has multiple issues. The comment claims that it was NULing (sic "null") the "rest". However, it has no idea how long the "rest" is, at this point. It was NULing BUFF_SZ - byte_offset + 1. After fixing the off-by-one / buffer overflow, it would be NULing BUFF_SZ - byte_offset. But that doesn't make any sense. The length of the first line has no relation to the length of the second line. For a quick-and-dirty test, add something like this just inside the while loop: memcpy(output, "Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0", sizeof("Exceeded MaxStartups\r\nnext blah1 blah2 blah3 blah4\0")); And, after the memmove(), add: printf("output='%s'\n", output); If you fix the memset() buffer overflow, it will output: output='ext blah1 blah2 blah3 ' As you can see, the first character is lost. If you then fix the memmove(), it will output: output='next blah1 blah2 blah3' Note that this is still losing the "blah4". After moving the memset() after byte_offset is set to the new strlen() of output, then it works correctly: output='next blah1 blah2 blah3 blah4' Signed-off-by: Richard Laager --- plugins/check_ssh.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index 9d0d7cde..fd082e11 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -273,12 +273,14 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ } if (version_control_string == NULL) { - /* move unconsumed data to beginning of buffer, null rest */ - memmove((void *)output, (void *)(output + byte_offset + 1), BUFF_SZ - len + 1); - memset(output + byte_offset + 1, 0, BUFF_SZ - byte_offset + 1); + /* move unconsumed data to beginning of buffer */ + memmove((void *)output, (void *)(output + byte_offset), BUFF_SZ - byte_offset); /*start reading from end of current line chunk on next recv*/ byte_offset = strlen(output); + + /* NUL the rest of the buffer */ + memset(output + byte_offset, 0, BUFF_SZ - byte_offset); } } else { byte_offset += recv_ret; -- cgit v1.2.3-74-g34f1 From 1f2acfd1c6577db6e3d385614922e32ac9fad03f Mon Sep 17 00:00:00 2001 From: Richard Laager Date: Fri, 11 Jul 2025 18:38:42 -0500 Subject: check_ssh: Correct type on len variable strlen() returns a size_t. Signed-off-by: Richard Laager --- plugins/check_ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index fd082e11..2c76fa84 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -255,7 +255,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ byte_offset = 0; char *index = NULL; - unsigned long len = 0; + size_t len = 0; while ((index = strchr(output + byte_offset, '\n')) != NULL) { /*Partition the buffer so that this line is a separate string, * by replacing the newline with NUL*/ -- cgit v1.2.3-74-g34f1 From c43135a5e5d7b03eaaf557581f7a8cbc86fa7b90 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle Date: Mon, 14 Jul 2025 09:37:51 +0200 Subject: check_ntp_peer: fix invalid conversion in printf --- plugins/check_ntp_peer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 6e76bf23..5c4ff386 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -707,11 +707,11 @@ int main(int argc, char *argv[]) { if (config.do_stratum) { if (sresult == STATE_WARNING) { - xasprintf(&result_line, "%s, stratum=%l (WARNING)", result_line, ntp_res.stratum); + xasprintf(&result_line, "%s, stratum=%li (WARNING)", result_line, ntp_res.stratum); } else if (sresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s, stratum=%l (CRITICAL)", result_line, ntp_res.stratum); + xasprintf(&result_line, "%s, stratum=%li (CRITICAL)", result_line, ntp_res.stratum); } else { - xasprintf(&result_line, "%s, stratum=%l", result_line, ntp_res.stratum); + xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum); } xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds)); } -- cgit v1.2.3-74-g34f1 From e570ce63634b57573aa84b12d84e7651d466a761 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 14 Jul 2025 23:35:52 +0200 Subject: check_curl: various small improvements --- plugins/check_curl.c | 134 +++++++++++++++++++++--------------------- plugins/check_curl.d/config.h | 12 ++-- 2 files changed, 74 insertions(+), 72 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 5b8a06be..88c5f3e7 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -53,7 +53,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "curl/curl.h" #include "curl/easy.h" -#include "picohttpparser.h" +#include "./picohttpparser/picohttpparser.h" #include "uriparser/Uri.h" @@ -164,7 +164,7 @@ static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); -static char *perfd_size(int page_len, int /*min_page_len*/); +static char *perfd_size(size_t page_len, int /*min_page_len*/); static void print_help(void); void print_usage(void); static void print_curl_version(void); @@ -185,10 +185,10 @@ static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -static int check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, - char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); -static int get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf); +static mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); @@ -213,7 +213,7 @@ int main(int argc, char **argv) { const check_curl_config config = tmp_config.config; /* set defaults */ - if (config.user_agent == NULL) { + if (strlen(config.user_agent) == 0) { snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", progname, NP_VERSION, VERSION, curl_version()); } @@ -224,7 +224,7 @@ int main(int argc, char **argv) { config.virtual_port ? config.virtual_port : config.server_port, config.server_url); } - exit(check_http(config)); + exit((int)check_http(config)); } #ifdef HAVE_SSL @@ -297,7 +297,7 @@ static char *string_statuscode(int major, int minor) { } /* Checks if the server 'reply' is one of the expected 'statuscodes' */ -static int expected_statuscode(const char *reply, const char *statuscodes) { +static bool expected_statuscode(const char *reply, const char *statuscodes) { char *expected; if ((expected = strdup(statuscodes)) == NULL) { @@ -305,10 +305,10 @@ static int expected_statuscode(const char *reply, const char *statuscodes) { } char *code; - int result = 0; + bool result = false; for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { - result = 1; + result = true; break; } } @@ -432,7 +432,7 @@ mp_state_enum check_http(check_curl_config config) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); - if (config.automatic_decompression) + if (config.automatic_decompression) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); @@ -440,6 +440,7 @@ mp_state_enum check_http(check_curl_config config) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ + } /* initialize buffer for body of the answer */ if (curlhelp_initwritebuffer(&body_buf) < 0) { @@ -448,7 +449,7 @@ mp_state_enum check_http(check_curl_config config) { body_buf_initialized = true; handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - (curl_write_callback)curlhelp_buffer_write_callback), + curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); @@ -460,7 +461,7 @@ mp_state_enum check_http(check_curl_config config) { header_buf_initialized = true; handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, - (curl_write_callback)curlhelp_buffer_write_callback), + curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER"); @@ -488,7 +489,7 @@ mp_state_enum check_http(check_curl_config config) { char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; if (config.use_ssl && config.host_name != NULL) { - CURLcode res; + int res; if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), @@ -572,14 +573,15 @@ mp_state_enum check_http(check_curl_config config) { char *force_host_header = NULL; /* check if Host header is explicitly set in options */ if (config.http_opt_headers_count) { - for (int i = 0; i < config.http_opt_headers_count; i++) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { force_host_header = config.http_opt_headers[i]; } } } - /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in anyway */ + /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in + * anyway */ char http_header[DEFAULT_BUFFER_SIZE]; if (config.host_name != NULL && force_host_header == NULL) { if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || @@ -599,7 +601,7 @@ mp_state_enum check_http(check_curl_config config) { /* attach additional headers supplied by the user */ /* optionally send any other header tag */ if (config.http_opt_headers_count) { - for (int i = 0; i < config.http_opt_headers_count; i++) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } /* This cannot be free'd here because a redirection will then try to access this and @@ -794,8 +796,8 @@ mp_state_enum check_http(check_curl_config config) { CURLOPT_POSTREDIR: method switch CURLINFO_REDIRECT_URL: custom redirect option CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols - CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option - here is nice like for expected page size? + CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range + option here is nice like for expected page size? */ } else { /* old style redirection is handled below */ @@ -863,8 +865,8 @@ mp_state_enum check_http(check_curl_config config) { /* cookie handling */ if (config.cookie_jar_file != NULL) { - /* enable reading cookies from a file, and if the filename is an empty string, only enable - * the curl cookie engine */ + /* enable reading cookies from a file, and if the filename is an empty string, only + * enable the curl cookie engine */ handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); @@ -902,7 +904,7 @@ mp_state_enum check_http(check_curl_config config) { die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - int result_ssl = STATE_OK; + mp_state_enum result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL if (config.use_ssl) { @@ -928,8 +930,9 @@ mp_state_enum check_http(check_curl_config config) { res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { # ifdef USE_OPENSSL - /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing - * We only check the first certificate and assume it's the one of the server + /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert + * parsing We only check the first certificate and assume it's the one of + * the server */ const char *raw_cert = NULL; for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { @@ -952,7 +955,7 @@ mp_state_enum check_http(check_curl_config config) { die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } BIO *cert_BIO = BIO_new(BIO_s_mem()); - BIO_write(cert_BIO, raw_cert, strlen(raw_cert)); + BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert)); cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); if (!cert) { snprintf( @@ -993,7 +996,7 @@ mp_state_enum check_http(check_curl_config config) { double total_time; handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - int page_len = get_content_length(&header_buf, &body_buf); + size_t page_len = get_content_length(&header_buf, &body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; @@ -1038,7 +1041,8 @@ mp_state_enum check_http(check_curl_config config) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); - /* we cannot know the major/minor version here for sure as we cannot parse the first line */ + /* we cannot know the major/minor version here for sure as we cannot parse the first + * line */ die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg); } status_line_initialized = true; @@ -1070,7 +1074,7 @@ mp_state_enum check_http(check_curl_config config) { config.show_body ? body_buf.buf : ""); } - int result = STATE_OK; + mp_state_enum result = STATE_OK; if (config.server_expect_yn) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); @@ -1225,7 +1229,7 @@ mp_state_enum check_http(check_curl_config config) { if ((config.max_page_len > 0) && (page_len > config.max_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too large, "), msg, page_len); strcpy(msg, tmp); @@ -1234,7 +1238,7 @@ mp_state_enum check_http(check_curl_config config) { } else if ((config.min_page_len > 0) && (page_len < config.min_page_len)) { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too small, "), msg, page_len); strcpy(msg, tmp); result = max_state_alt(STATE_WARNING, result); } @@ -1253,8 +1257,8 @@ mp_state_enum check_http(check_curl_config config) { /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), * msg); */ - die(max_state_alt(result, result_ssl), - "HTTP %s: %s %d %s%s%s - %d bytes in %.3f second response time %s|%s\n%s%s", + die((int)max_state_alt(result, result_ssl), + "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), status_line.http_code, status_line.msg, strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, @@ -1582,7 +1586,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { case 'H': /* virtual host */ result.config.host_name = strdup(optarg); char *p; - int host_name_length; + size_t host_name_length; if (result.config.host_name[0] == '[') { if ((p = strstr(result.config.host_name, "]:")) != NULL) { /* [IPv6]:port */ result.config.virtual_port = atoi(p + 2); @@ -1734,31 +1738,31 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.ssl_version = CURL_SSLVERSION_SSLv2; } else if (optarg[0] == '3') { result.config.ssl_version = CURL_SSLVERSION_SSLv3; - } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) + } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - else if (!strcmp(optarg, "1.1")) + } else if (!strcmp(optarg, "1.1")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - else if (!strcmp(optarg, "1.2")) + } else if (!strcmp(optarg, "1.2")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - else if (!strcmp(optarg, "1.3")) + } else if (!strcmp(optarg, "1.3")) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; # else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; # endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ - else { + } else { usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " "(with optional '+' suffix)")); } @@ -1914,7 +1918,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - result.config.min_page_len = atoi(tmp); + result.config.min_page_len = atol(tmp); } tmp = strtok(NULL, ":"); @@ -1922,10 +1926,10 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf("Bad format: try \"-m min:max\"\n"); exit(STATE_WARNING); } else { - result.config.max_page_len = atoi(tmp); + result.config.max_page_len = atol(tmp); } } else { - result.config.min_page_len = atoi(optarg); + result.config.min_page_len = atol(optarg); } break; } @@ -2042,8 +2046,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", thlds->warning, - thlds->warning ? thlds->warning->end : 0, thlds->critical, + return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), + thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); } @@ -2072,7 +2076,7 @@ char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { true, socket_timeout); } -char *perfd_size(int page_len, int min_page_len) { +char *perfd_size(size_t page_len, int min_page_len) { return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, true, 0, false, 0); } @@ -2120,9 +2124,8 @@ void print_help(void) { printf(" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); - printf( - " %s\n", - _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are also accepted.")); + printf(" %s\n", _("1.2 = TLSv1.2, 1.3 = TLSv1.3). With a '+' suffix, newer versions are " + "also accepted.")); printf(" %s\n", _("Note: SSLv2, SSLv3, TLSv1.0 and TLSv1.1 are deprecated and are usually " "disabled in libcurl")); printf(" %s\n", "--sni"); @@ -2205,9 +2208,8 @@ void print_help(void) { printf(" %s\n", "-A, --useragent=STRING"); printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); printf(" %s\n", "-k, --header=STRING"); - printf( - " %s\n", - _("Any other tags to be sent in http header. Use multiple times for additional headers")); + printf(" %s\n", _("Any other tags to be sent in http header. Use multiple times for " + "additional headers")); printf(" %s\n", "-E, --extended-perfdata"); printf(" %s\n", _("Print additional performance data")); printf(" %s\n", "-B, --show-body"); @@ -2357,7 +2359,7 @@ void print_curl_version(void) { printf("%s\n", curl_version()); } int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) { buf->bufsize = DEFAULT_BUFFER_SIZE; buf->buflen = 0; - buf->buf = (char *)malloc((size_t)buf->bufsize); + buf->buf = (char *)malloc(buf->bufsize); if (buf->buf == NULL) { return -1; } @@ -2372,7 +2374,7 @@ size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, v buf->buf = (char *)realloc(buf->buf, buf->bufsize); if (buf->buf == NULL) { fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); - return -1; + return 0; } } @@ -2380,7 +2382,7 @@ size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, v buf->buflen += size * nmemb; buf->buf[buf->buflen] = '\0'; - return (int)(size * nmemb); + return size * nmemb; } size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { @@ -2391,7 +2393,7 @@ size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, vo memcpy(buffer, buf->buf + buf->pos, n); buf->pos += n; - return (int)n; + return n; } void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { @@ -2401,7 +2403,7 @@ void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) { buf->buflen = datalen; - buf->buf = (char *)malloc((size_t)buf->buflen); + buf->buf = (char *)malloc(buf->buflen); if (buf->buf == NULL) { return -1; } @@ -2568,8 +2570,8 @@ char *get_header_value(const struct phr_header *headers, const size_t nof_header return NULL; } -int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE], - int maximum_age) { +mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, + char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; curlhelp_statusline status_line; @@ -2585,7 +2587,7 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D char *server_date = get_header_value(headers, nof_headers, "date"); char *document_date = get_header_value(headers, nof_headers, "last-modified"); - int date_result = STATE_OK; + mp_state_enum date_result = STATE_OK; if (!server_date || !*server_date) { char tmp[DEFAULT_BUFFER_SIZE]; @@ -2634,20 +2636,20 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D date_result = max_state_alt(STATE_CRITICAL, date_result); } else if (doc_data < srv_data - maximum_age) { - int n = (srv_data - doc_data); - if (n > (60 * 60 * 24 * 2)) { + time_t last_modified = (srv_data - doc_data); + if (last_modified > (60 * 60 * 24 * 2)) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, - ((float)n) / (60 * 60 * 24)); + ((float)last_modified) / (60 * 60 * 24)); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); } else { char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, - n / (60 * 60), (n / 60) % 60, n % 60); + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), *msg, + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); strcpy(*msg, tmp); date_result = max_state_alt(STATE_CRITICAL, date_result); @@ -2665,8 +2667,8 @@ int check_document_dates(const curlhelp_write_curlbuf *header_buf, char (*msg)[D return date_result; } -int get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf) { +size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf) { struct phr_header headers[255]; size_t nof_headers = 255; size_t msglen; diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 9085fbbb..90ea3810 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -48,7 +48,7 @@ typedef struct { char *http_method; char user_agent[DEFAULT_BUFFER_SIZE]; char **http_opt_headers; - int http_opt_headers_count; + size_t http_opt_headers_count; char *http_post_data; int max_depth; char *http_content_type; @@ -63,7 +63,7 @@ typedef struct { int maximum_age; char regexp[MAX_RE_SIZE]; - int state_regex; + mp_state_enum state_regex; bool invert_regex; bool verify_peer_and_host; bool check_cert; @@ -71,13 +71,13 @@ typedef struct { int days_till_exp_warn; int days_till_exp_crit; thresholds *thlds; - int min_page_len; - int max_page_len; + size_t min_page_len; + size_t max_page_len; char server_expect[MAX_INPUT_BUFFER]; bool server_expect_yn; char string_expect[MAX_INPUT_BUFFER]; char header_expect[MAX_INPUT_BUFFER]; - int onredirect; + mp_state_enum onredirect; bool show_extended_perfdata; bool show_body; @@ -100,7 +100,7 @@ check_curl_config check_curl_config_init() { .use_ssl = false, .ssl_version = CURL_SSLVERSION_DEFAULT, .http_method = NULL, - .user_agent = {}, + .user_agent = {'\0'}, .http_opt_headers = NULL, .http_opt_headers_count = 0, .http_post_data = NULL, -- cgit v1.2.3-74-g34f1 From d2bea1d288328d2d387d587b38a0efeba1becc97 Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Wed, 23 Jul 2025 15:22:45 +0200 Subject: CI: Adding workflow_dispatch --- .github/workflows/codeql-analysis.yml | 1 + .github/workflows/spellcheck.yml | 1 + .github/workflows/test.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c402e0cf..e748d2eb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,6 +13,7 @@ name: "CodeQL" on: + workflow_dispatch: {} push: branches: [master] pull_request: diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 72f7c7eb..c8714e64 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -2,6 +2,7 @@ name: Spellcheck on: + workflow_dispatch: {} # Run for pushes on any branch push: branches: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ce0ec547..146d91b6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,7 @@ name: Tests on: + workflow_dispatch: {} push: branches: - '*' -- cgit v1.2.3-74-g34f1 From 61a68da144b726668196ba5cb01fcce9e99f0fdb Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Thu, 24 Jul 2025 10:28:03 +0200 Subject: Adding tmate optional to manual dispatch --- .github/workflows/test-next.yml | 14 +++++++++++++- .github/workflows/test.yml | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml index fd59e85d..7ca255c9 100644 --- a/.github/workflows/test-next.yml +++ b/.github/workflows/test-next.yml @@ -2,7 +2,13 @@ name: Tests Debian:Testing and Fedora:Rawhide on: - workflow_dispatch: {} + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false push: branches-ignore: - '*' @@ -25,6 +31,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol @@ -60,6 +69,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 146d91b6..f9919b2d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,13 @@ name: Tests on: - workflow_dispatch: {} + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false push: branches: - '*' @@ -23,6 +29,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol @@ -61,6 +70,9 @@ jobs: steps: - name: Git clone repository uses: actions/checkout@v4 + - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} - name: Run the tests on ${{ matrix.distro }} run: | docker volume create --driver local --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 tmp-vol -- cgit v1.2.3-74-g34f1 From 4966b920a2392484a6ae83a4a499c24a2942c832 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:29:08 +0200 Subject: General smal improvements to the lib logic --- lib/utils_base.c | 15 ++++++++------- lib/utils_base.h | 5 +++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/utils_base.c b/lib/utils_base.c index c49a473f..60103614 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -25,6 +25,7 @@ *****************************************************************************/ #include "../plugins/common.h" +#include "states.h" #include #include "utils_base.h" #include @@ -256,21 +257,21 @@ bool check_range(double value, range *my_range) { yes = false; } - if (my_range->end_infinity == false && my_range->start_infinity == false) { + if (!my_range->end_infinity&& !my_range->start_infinity) { if ((my_range->start <= value) && (value <= my_range->end)) { return no; } return yes; } - if (my_range->start_infinity == false && my_range->end_infinity == true) { + if (!my_range->start_infinity && my_range->end_infinity) { if (my_range->start <= value) { return no; } return yes; } - if (my_range->start_infinity == true && my_range->end_infinity == false) { + if (my_range->start_infinity && !my_range->end_infinity ) { if (value <= my_range->end) { return no; } @@ -280,14 +281,14 @@ bool check_range(double value, range *my_range) { } /* Returns status */ -int get_status(double value, thresholds *my_thresholds) { +mp_state_enum get_status(double value, thresholds *my_thresholds) { if (my_thresholds->critical != NULL) { - if (check_range(value, my_thresholds->critical) == true) { + if (check_range(value, my_thresholds->critical)) { return STATE_CRITICAL; } } if (my_thresholds->warning != NULL) { - if (check_range(value, my_thresholds->warning) == true) { + if (check_range(value, my_thresholds->warning)) { return STATE_WARNING; } } @@ -395,7 +396,7 @@ char *np_extract_value(const char *varlist, const char *name, char sep) { return value; } -const char *state_text(int result) { +const char *state_text(mp_state_enum result) { switch (result) { case STATE_OK: return "OK"; diff --git a/lib/utils_base.h b/lib/utils_base.h index 123066f8..8fb114c2 100644 --- a/lib/utils_base.h +++ b/lib/utils_base.h @@ -7,6 +7,7 @@ #include "./perfdata.h" #include "./thresholds.h" +#include "states.h" #ifndef USE_OPENSSL @@ -55,7 +56,7 @@ void set_thresholds(thresholds **, char *, char *); void print_thresholds(const char *, thresholds *); bool check_range(double, range *); bool mp_check_range(mp_perfdata_value, mp_range); -int get_status(double, thresholds *); +mp_state_enum get_status(double, thresholds *); /* Handle timeouts */ extern int timeout_state; @@ -107,6 +108,6 @@ void np_state_write_string(time_t, char *); void np_init(char *, int argc, char **argv); void np_set_args(int argc, char **argv); void np_cleanup(void); -const char *state_text(int); +const char *state_text(mp_state_enum); #endif /* _UTILS_BASE_ */ -- cgit v1.2.3-74-g34f1 From b54869391faab7ef91586c81de21f18a61bac5aa Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:29:48 +0200 Subject: Quick save --- plugins/check_curl.c | 51 ++++++++++++++++++++++--------------------- plugins/check_curl.d/config.h | 3 +++ 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 88c5f3e7..94b726d0 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -127,8 +127,6 @@ static curlhelp_statusline status_line; static bool put_buf_initialized = false; static curlhelp_read_curlbuf put_buf; -static struct curl_slist *server_ips = NULL; // TODO maybe unused -static int redir_depth = 0; // Maybe global static CURL *curl; static struct curl_slist *header_list = NULL; static long code; @@ -138,7 +136,6 @@ typedef union { struct curl_slist *to_info; struct curl_certinfo *to_certinfo; } cert_ptr_union; -static cert_ptr_union cert_ptr; static bool is_openssl_callback = false; static bool add_sslctx_verify_fun = false; @@ -146,7 +143,6 @@ static bool add_sslctx_verify_fun = false; static X509 *cert = NULL; #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static int address_family = AF_UNSPEC; static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; typedef struct { @@ -156,8 +152,11 @@ typedef struct { static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static void handle_curl_option_return_code(CURLcode res, const char *option); -static mp_state_enum check_http(check_curl_config /*config*/); -static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/); +static mp_state_enum check_http(check_curl_config /*config*/, int redir_depth); + +static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, + int redir_depth); + static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); @@ -224,7 +223,9 @@ int main(int argc, char **argv) { config.virtual_port ? config.virtual_port : config.server_port, config.server_url); } - exit((int)check_http(config)); + int redir_depth = 0; + + exit((int)check_http(config, redir_depth)); } #ifdef HAVE_SSL @@ -326,10 +327,10 @@ void handle_curl_option_return_code(CURLcode res, const char *option) { } } -int lookup_host(const char *host, char *buf, size_t buflen) { +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); - hints.ai_family = address_family; + hints.ai_family = addr_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_CANONNAME; @@ -408,7 +409,7 @@ static void cleanup(void) { put_buf_initialized = false; } -mp_state_enum check_http(check_curl_config config) { +mp_state_enum check_http(check_curl_config config, int redir_depth) { /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); @@ -448,8 +449,7 @@ mp_state_enum check_http(check_curl_config config) { } body_buf_initialized = true; handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - curlhelp_buffer_write_callback), + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA"); @@ -460,8 +460,7 @@ mp_state_enum check_http(check_curl_config config) { } header_buf_initialized = true; handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, - curlhelp_buffer_write_callback), + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER"); @@ -490,7 +489,8 @@ mp_state_enum check_http(check_curl_config config) { char addrstr[DEFAULT_BUFFER_SIZE / 2]; if (config.use_ssl && config.host_name != NULL) { int res; - if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2)) != 0) { + if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + config.sin_family)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), config.server_address, res, gai_strerror(res)); @@ -810,16 +810,16 @@ mp_state_enum check_http(check_curl_config config) { } /* IPv4 or IPv6 forced DNS resolution */ - if (address_family == AF_UNSPEC) { + if (config.sin_family == AF_UNSPEC) { handle_curl_option_return_code( curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); - } else if (address_family == AF_INET) { + } else if (config.sin_family == AF_INET) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - else if (address_family == AF_INET6) { + else if (config.sin_family == AF_INET6) { handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); } @@ -889,8 +889,7 @@ mp_state_enum check_http(check_curl_config config) { /* free header and server IP resolve lists, we don't need it anymore */ curl_slist_free_all(header_list); header_list = NULL; - curl_slist_free_all(server_ips); - server_ips = NULL; + if (host) { curl_slist_free_all(host); host = NULL; @@ -926,6 +925,7 @@ mp_state_enum check_http(check_curl_config config) { } else { struct curl_slist *slist; + cert_ptr_union cert_ptr = {0}; cert_ptr.to_info = NULL; res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { @@ -1103,7 +1103,7 @@ mp_state_enum check_http(check_curl_config config) { * back here, we are in the same status as with * the libcurl method */ - redir(&header_buf, config); + redir(&header_buf, config, redir_depth); } } else { /* this is a specific code in the command line to @@ -1287,7 +1287,8 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { return buf; } -void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { +void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int redir_depth) { + curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1443,7 +1444,7 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config) { */ cleanup(); - check_http(config); + check_http(config, redir_depth); } /* check whether a file exists */ @@ -1899,11 +1900,11 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case '4': - address_family = AF_INET; + result.config.sin_family = AF_INET; break; case '6': #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - address_family = AF_INET6; + result.config.sin_family = AF_INET6; #else usage4(_("IPv6 support not available")); #endif diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 90ea3810..43be0306 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -6,6 +6,7 @@ #include "thresholds.h" #include #include +#include #include "curl/curl.h" enum { @@ -37,6 +38,7 @@ typedef struct { unsigned short virtual_port; char *host_name; char *server_url; + sa_family_t sin_family; bool automatic_decompression; bool haproxy_protocol; @@ -91,6 +93,7 @@ check_curl_config check_curl_config_init() { .virtual_port = 0, .host_name = NULL, .server_url = strdup(DEFAULT_SERVER_URL), + .sin_family = AF_UNSPEC, .automatic_decompression = false, .haproxy_protocol = false, -- cgit v1.2.3-74-g34f1 From 69925c782bf70d267ea14a57d46b5390f5555b8f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:34:01 +0200 Subject: check_ssh: fix data type to allow for error checking --- plugins/check_ssh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index 2c76fa84..f93127ce 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -245,7 +245,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ char *output = (char *)calloc(BUFF_SZ + 1, sizeof(char)); char *buffer = NULL; - size_t recv_ret = 0; + ssize_t recv_ret = 0; char *version_control_string = NULL; size_t byte_offset = 0; while ((version_control_string == NULL) && @@ -283,7 +283,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ memset(output + byte_offset, 0, BUFF_SZ - byte_offset); } } else { - byte_offset += recv_ret; + byte_offset += (size_t)recv_ret; } } -- cgit v1.2.3-74-g34f1 From 3c53bf623d89650ac450be2518d17276a29247cc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:34:29 +0200 Subject: check_ssh: Fix format expression --- plugins/check_ssh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index f93127ce..af7089a7 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -289,7 +289,7 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ if (recv_ret < 0) { connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); - xasprintf(&connection_sc.output, "%s", "SSH CRITICAL - %s", strerror(errno)); + xasprintf(&connection_sc.output, "%s - %s", "SSH CRITICAL - ", strerror(errno)); mp_add_subcheck_to_check(overall, connection_sc); return OK; } -- cgit v1.2.3-74-g34f1 From a69dff15222ad43c56f0142e20d97ee51c2e6697 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:35:13 +0200 Subject: check_ssh: Put variable in the correct scope --- plugins/check_ssh.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index af7089a7..b4a98cdf 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -255,12 +255,11 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ byte_offset = 0; char *index = NULL; - size_t len = 0; while ((index = strchr(output + byte_offset, '\n')) != NULL) { /*Partition the buffer so that this line is a separate string, * by replacing the newline with NUL*/ output[(index - output)] = '\0'; - len = strlen(output + byte_offset); + size_t len = strlen(output + byte_offset); if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { /*if the string starts with SSH-, this _should_ be a valid version control string*/ -- cgit v1.2.3-74-g34f1 From 2757550558d509aa5c95d8834ee76d803e110161 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:35:23 +0200 Subject: clang-format --- plugins/check_ssh.c | 72 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/plugins/check_ssh.c b/plugins/check_ssh.c index b4a98cdf..f6c8d551 100644 --- a/plugins/check_ssh.c +++ b/plugins/check_ssh.c @@ -57,7 +57,8 @@ static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv* static void print_help(void); void print_usage(void); -static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, char *remote_protocol); +static int ssh_connect(mp_check *overall, char *haddr, int hport, char *remote_version, + char *remote_protocol); int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -85,7 +86,8 @@ int main(int argc, char **argv) { alarm(socket_timeout); /* ssh_connect exits if error is found */ - ssh_connect(&overall, config.server_name, config.port, config.remote_version, config.remote_protocol); + ssh_connect(&overall, config.server_name, config.port, config.remote_version, + config.remote_protocol); alarm(0); @@ -96,19 +98,20 @@ int main(int argc, char **argv) { /* process command-line arguments */ process_arguments_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = {{"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"host", required_argument, 0, 'H'}, /* backward compatibility */ - {"hostname", required_argument, 0, 'H'}, - {"port", required_argument, 0, 'p'}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"timeout", required_argument, 0, 't'}, - {"verbose", no_argument, 0, 'v'}, - {"remote-version", required_argument, 0, 'r'}, - {"remote-protocol", required_argument, 0, 'P'}, - {"output-format", required_argument, 0, output_format_index}, - {0, 0, 0, 0}}; + static struct option longopts[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"host", required_argument, 0, 'H'}, /* backward compatibility */ + {"hostname", required_argument, 0, 'H'}, + {"port", required_argument, 0, 'p'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"timeout", required_argument, 0, 't'}, + {"verbose", no_argument, 0, 'v'}, + {"remote-version", required_argument, 0, 'r'}, + {"remote-protocol", required_argument, 0, 'P'}, + {"output-format", required_argument, 0, output_format_index}, + {0, 0, 0, 0}}; process_arguments_wrapper result = { .config = check_ssh_config_init(), @@ -228,7 +231,8 @@ process_arguments_wrapper process_arguments(int argc, char **argv) { * *-----------------------------------------------------------------------*/ -int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, char *desired_remote_protocol) { +int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_version, + char *desired_remote_protocol) { struct timeval tv; gettimeofday(&tv, NULL); @@ -238,7 +242,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ mp_subcheck connection_sc = mp_subcheck_init(); if (result != STATE_OK) { connection_sc = mp_set_subcheck_state(connection_sc, STATE_CRITICAL); - xasprintf(&connection_sc.output, "Failed to establish TCP connection to Host %s and Port %d", haddr, hport); + xasprintf(&connection_sc.output, + "Failed to establish TCP connection to Host %s and Port %d", haddr, hport); mp_add_subcheck_to_check(overall, connection_sc); return result; } @@ -249,7 +254,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ char *version_control_string = NULL; size_t byte_offset = 0; while ((version_control_string == NULL) && - (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), 0) > 0)) { + (recv_ret = recv(socket, output + byte_offset, (unsigned long)(BUFF_SZ - byte_offset), + 0) > 0)) { if (strchr(output, '\n')) { /* we've got at least one full line, start parsing*/ byte_offset = 0; @@ -262,7 +268,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ size_t len = strlen(output + byte_offset); if ((len >= 4) && (strncmp(output + byte_offset, "SSH-", 4) == 0)) { - /*if the string starts with SSH-, this _should_ be a valid version control string*/ + /*if the string starts with SSH-, this _should_ be a valid version control + * string*/ version_control_string = output + byte_offset; break; } @@ -334,7 +341,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ * "1.x" (e.g., "1.5" or "1.3")." * - RFC 4253:5 */ - char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ + char *ssh_server = ssh_proto + strspn(ssh_proto, "0123456789.") + + 1; /* (+1 for the '-' separating protoversion from softwareversion) */ /* If there's a space in the version string, whatever's after the space is a comment * (which is NOT part of the server name/version)*/ @@ -346,13 +354,15 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ mp_subcheck protocol_validity_sc = mp_subcheck_init(); if (strlen(ssh_proto) == 0 || strlen(ssh_server) == 0) { protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_CRITICAL); - xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", version_control_string); + xasprintf(&protocol_validity_sc.output, "Invalid protocol version control string %s", + version_control_string); mp_add_subcheck_to_check(overall, protocol_validity_sc); return OK; } protocol_validity_sc = mp_set_subcheck_state(protocol_validity_sc, STATE_OK); - xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", version_control_string); + xasprintf(&protocol_validity_sc.output, "Valid protocol version control string %s", + version_control_string); mp_add_subcheck_to_check(overall, protocol_validity_sc); ssh_proto[strspn(ssh_proto, "0123456789. ")] = 0; @@ -367,8 +377,8 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ if (desired_remote_version && strcmp(desired_remote_version, ssh_server)) { mp_subcheck remote_version_sc = mp_subcheck_init(); remote_version_sc = mp_set_subcheck_state(remote_version_sc, STATE_CRITICAL); - xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), ssh_server, ssh_proto, - desired_remote_version); + xasprintf(&remote_version_sc.output, _("%s (protocol %s) version mismatch, expected '%s'"), + ssh_server, ssh_proto, desired_remote_version); close(socket); mp_add_subcheck_to_check(overall, remote_version_sc); return OK; @@ -386,11 +396,13 @@ int ssh_connect(mp_check *overall, char *haddr, int hport, char *desired_remote_ if (desired_remote_protocol && strcmp(desired_remote_protocol, ssh_proto)) { protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_CRITICAL); - xasprintf(&protocol_version_sc.output, _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, ssh_proto, - desired_remote_protocol); + xasprintf(&protocol_version_sc.output, + _("%s (protocol %s) protocol version mismatch, expected '%s'"), ssh_server, + ssh_proto, desired_remote_protocol); } else { protocol_version_sc = mp_set_subcheck_state(protocol_version_sc, STATE_OK); - xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", ssh_server, ssh_proto); + xasprintf(&protocol_version_sc.output, "SSH server version: %s (protocol version: %s)", + ssh_server, ssh_proto); } mp_add_subcheck_to_check(overall, protocol_version_sc); @@ -423,7 +435,8 @@ void print_help(void) { printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(" %s\n", "-r, --remote-version=STRING"); - printf(" %s\n", _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); + printf(" %s\n", + _("Alert if string doesn't match expected server version (ex: OpenSSH_3.9p1)")); printf(" %s\n", "-P, --remote-protocol=STRING"); printf(" %s\n", _("Alert if protocol doesn't match expected protocol version (ex: 2.0)")); @@ -436,5 +449,6 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf("%s [-4|-6] [-t ] [-r ] [-p ] --hostname \n", progname); + printf("%s [-4|-6] [-t ] [-r ] [-p ] --hostname \n", + progname); } -- cgit v1.2.3-74-g34f1 From 278954117cabd8e76941d4191a2692bfdeb39372 Mon Sep 17 00:00:00 2001 From: Jan Wagner Date: Fri, 1 Aug 2025 21:33:39 +0200 Subject: (Re)construct PLATFORM_ID as it's droped since Fedora 43 See https://fedoraproject.org/wiki/Changes/Drop_PLATFORM_ID?#Drop_PLATFORM_ID --- .github/os_detect.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/os_detect.sh b/.github/os_detect.sh index 47c762d3..3c5956de 100644 --- a/.github/os_detect.sh +++ b/.github/os_detect.sh @@ -22,4 +22,7 @@ else return 1 fi export distro_id=$(grep '^ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') +export version_id=$(grep '^VERSION_ID=' $os_release_file|awk -F = '{print $2}'|sed 's/\"//g') export platform_id=$(grep '^PLATFORM_ID=' /etc/os-release|awk -F = '{print $2}'|sed 's/\"//g'| cut -d":" -f2) +# Fedora dropped PLATFORM_ID: https://fedoraproject.org/wiki/Changes/Drop_PLATFORM_ID?#Drop_PLATFORM_ID +if [ -z $platform_id ]; then export platform_id=$(echo ${distro_id:0:1}${version_id}); fi -- cgit v1.2.3-74-g34f1 From fb39f96ac6f72bb56d17f3e8694134dfea9186e9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 11 Aug 2025 21:49:20 +0200 Subject: check_users: Use sd_get_uids instead of sd_get_session Previously check_users in combination with systemd used sd_get_sessions (3) to aquire the number of users, probably with the idea that every users opens a session. Turns out, that a user can have multiple sessions and we only really want to know how many users there are. This commit changes to sd_get_uids (3) to achieve that target. --- plugins/check_users.d/users.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_users.d/users.c b/plugins/check_users.d/users.c index 95c05d6f..a8b168a0 100644 --- a/plugins/check_users.d/users.c +++ b/plugins/check_users.d/users.c @@ -61,7 +61,7 @@ get_num_of_users_wrapper get_num_of_users_systemd() { // Test whether we booted with systemd if (sd_booted() > 0) { - int users = sd_get_sessions(NULL); + int users = sd_get_uids(NULL); if (users >= 0) { // Success result.users = users; -- cgit v1.2.3-74-g34f1 From fc0f176f0deb4f473553ae71eac8ee33654d62e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:29:24 +0000 Subject: build(deps): bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/spellcheck.yml | 2 +- .github/workflows/test-next.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e748d2eb..57487eed 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index c8714e64..14b82781 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Codespell uses: codespell-project/actions-codespell@v2 with: diff --git a/.github/workflows/test-next.yml b/.github/workflows/test-next.yml index 7ca255c9..0e69c251 100644 --- a/.github/workflows/test-next.yml +++ b/.github/workflows/test-next.yml @@ -30,7 +30,7 @@ jobs: prepare: .github/prepare_debian.sh steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} @@ -68,7 +68,7 @@ jobs: - {"distro": "fedora:rawhide", "build": ".github/mock.sh"} steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9919b2d..1ac8aaf3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: prepare: .github/prepare_debian.sh steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} @@ -69,7 +69,7 @@ jobs: # - {"distro": "oraclelinux:9", "build": ".github/mock.sh"} steps: - name: Git clone repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup tmate session, see https://github.com/marketplace/actions/debugging-with-tmate uses: mxschmitt/action-tmate@v3 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} -- cgit v1.2.3-74-g34f1 From 7fe6ac8d08a2baf63db57fd33185224df7e18e27 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:28:04 +0200 Subject: rebuild check_snmp --- plugins/Makefile.am | 3 + plugins/check_snmp.c | 1654 ++++++++++++++++++----------------------- plugins/check_snmp.d/config.h | 107 +++ 3 files changed, 838 insertions(+), 926 deletions(-) create mode 100644 plugins/check_snmp.d/config.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 5994b405..765e2687 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -78,6 +78,7 @@ EXTRA_DIST = t \ check_ping.d \ check_by_ssh.d \ check_smtp.d \ + check_snmp.d \ check_mysql.d \ check_ntp_time.d \ check_dig.d \ @@ -152,6 +153,8 @@ check_procs_LDADD = $(BASEOBJS) check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) check_real_LDADD = $(NETLIBS) check_snmp_LDADD = $(BASEOBJS) +check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs` +check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags` check_smtp_LDADD = $(SSLOBJS) check_ssh_LDADD = $(NETLIBS) check_swap_SOURCES = check_swap.c check_swap.d/swap.c diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index c1d8e2dd..6c9ed959 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -32,222 +32,71 @@ const char *progname = "check_snmp"; const char *copyright = "1999-2024"; const char *email = "devel@monitoring-plugins.org"; -#include "common.h" -#include "runcmd.h" -#include "utils.h" -#include "utils_cmd.h" - -#define DEFAULT_COMMUNITY "public" -#define DEFAULT_PORT "161" -#define DEFAULT_MIBLIST "ALL" -#define DEFAULT_PROTOCOL "1" -#define DEFAULT_RETRIES 5 -#define DEFAULT_AUTH_PROTOCOL "MD5" -#define DEFAULT_PRIV_PROTOCOL "DES" -#define DEFAULT_DELIMITER "=" -#define DEFAULT_OUTPUT_DELIMITER " " -#define DEFAULT_BUFFER_SIZE 100 - -#define mark(a) ((a) != 0 ? "*" : "") - -#define CHECK_UNDEF 0 -#define CRIT_PRESENT 1 -#define CRIT_STRING 2 -#define CRIT_REGEX 4 -#define WARN_PRESENT 8 - -#define OID_COUNT_STEP 8 +#include "./common.h" +#include "./runcmd.h" +#include "./utils.h" +#include "../lib/states.h" +#include "../lib/utils_cmd.h" +#include "../lib/thresholds.h" +#include "../lib/utils_base.h" +#include "../lib/output.h" +#include "../lib/perfdata.h" + +#include +#include +#include +#include + +#include "check_snmp.d/config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../gl/regex.h" +#include + +const char DEFAULT_COMMUNITY[] = "public"; +const char DEFAULT_MIBLIST[] = "ALL"; +#define DEFAULT_AUTH_PROTOCOL "MD5" +#define DEFAULT_PRIV_PROTOCOL "DES" +#define DEFAULT_DELIMITER "=" +#define DEFAULT_BUFFER_SIZE 100 /* Longopts only arguments */ -#define L_CALCULATE_RATE CHAR_MAX + 1 -#define L_RATE_MULTIPLIER CHAR_MAX + 2 #define L_INVERT_SEARCH CHAR_MAX + 3 #define L_OFFSET CHAR_MAX + 4 #define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 +#define L_CONNECTION_PREFIX CHAR_MAX + 6 -/* Gobble to string - stop incrementing c when c[0] match one of the - * characters in s */ -#define GOBBLE_TOS(c, s) \ - while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \ - c++; \ - } -/* Given c, keep track of backslashes (bk) and double-quotes (dq) - * from c[0] */ -#define COUNT_SEQ(c, bk, dq) \ - switch (c[0]) { \ - case '\\': \ - if (bk) \ - bk--; \ - else \ - bk++; \ - break; \ - case '"': \ - if (!dq) { \ - dq++; \ - } else if (!bk) { \ - dq--; \ - } else { \ - bk--; \ - } \ - break; \ - } +typedef struct proces_arguments_wrapper { + int errorcode; + check_snmp_config config; +} process_arguments_wrapper; -static int process_arguments(int, char **); -static int validate_arguments(void); -static char *thisarg(char *str); -static char *nextarg(char *str); +static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); +char *trim_whitespaces_and_check_quoting(char *str); +char *get_next_argument(char *str); void print_usage(void); -static void print_help(void); -static char *multiply(char *str); - -#include "regex.h" -static char regex_expect[MAX_INPUT_BUFFER] = ""; -static regex_t preg; -static regmatch_t pmatch[10]; -static char errbuf[MAX_INPUT_BUFFER] = ""; -static char perfstr[MAX_INPUT_BUFFER] = "| "; -static int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; -static int eflags = 0; -static int errcode, excode; - -static char *server_address = NULL; -static char *community = NULL; -static char **contextargs = NULL; -static char *context = NULL; -static char **authpriv = NULL; -static char *proto = NULL; -static char *seclevel = NULL; -static char *secname = NULL; -static char *authproto = NULL; -static char *privproto = NULL; -static char *authpasswd = NULL; -static char *privpasswd = NULL; -static int nulloid = STATE_UNKNOWN; -static char **oids = NULL; -static size_t oids_size = 0; -static char *label; -static char *units; -static char *port; -static char *snmpcmd; -static char string_value[MAX_INPUT_BUFFER] = ""; -static int invert_search = 0; -static char **labels = NULL; -static char **unitv = NULL; -static size_t nlabels = 0; -static size_t labels_size = OID_COUNT_STEP; -static size_t nunits = 0; -static size_t unitv_size = OID_COUNT_STEP; -static size_t numoids = 0; -static int numauthpriv = 0; -static int numcontext = 0; +void print_help(void); +char *multiply(char *str, double multiplier, char *fmt_str); + static int verbose = 0; -static bool usesnmpgetnext = false; -static char *warning_thresholds = NULL; -static char *critical_thresholds = NULL; -static thresholds **thlds; -static size_t thlds_size = OID_COUNT_STEP; -static double *response_value; -static size_t response_size = OID_COUNT_STEP; -static int retries = 0; -static int *eval_method; -static size_t eval_size = OID_COUNT_STEP; -static char *delimiter; -static char *output_delim; -static char *miblist = NULL; -static bool needmibs = false; -static int calculate_rate = 0; -static double offset = 0.0; -static int rate_multiplier = 1; -static state_data *previous_state; -static double *previous_value; -static size_t previous_size = OID_COUNT_STEP; -static int perf_labels = 1; -static char *ip_version = ""; -static double multiplier = 1.0; -static char *fmtstr = ""; -static bool fmtstr_set = false; -static char buffer[DEFAULT_BUFFER_SIZE]; -static bool ignore_mib_parsing_errors = false; - -static char *fix_snmp_range(char *th) { - double left; - double right; - char *colon; - char *ret; - - if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0') - return th; - - left = strtod(th, NULL); - right = strtod(colon + 1, NULL); - if (right >= left) - return th; - - if ((ret = malloc(strlen(th) + 2)) == NULL) - die(STATE_UNKNOWN, _("Cannot malloc")); - *colon = '\0'; - sprintf(ret, "@%s:%s", colon + 1, th); - free(th); - return ret; -} int main(int argc, char **argv) { - int len; - int total_oids; - size_t line; - unsigned int bk_count = 0; - unsigned int dq_count = 0; - int iresult = STATE_UNKNOWN; - int result = STATE_UNKNOWN; - int return_code = 0; - int external_error = 0; - char **command_line = NULL; - char *cl_hidden_auth = NULL; - char *oidname = NULL; - char *response = NULL; - char *mult_resp = NULL; - char *outbuff; - char *ptr = NULL; - char *show = NULL; - char *th_warn = NULL; - char *th_crit = NULL; - char type[8] = ""; - output chld_out; - output chld_err; - char *previous_string = NULL; - char *ap = NULL; - char *state_string = NULL; - size_t response_length; - size_t current_length; - size_t string_length; - char *temp_string = NULL; - char *quote_string = NULL; - time_t current_time; - double temp_double; - time_t duration; - char *conv = "12345678"; - int is_counter = 0; - setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - labels = malloc(labels_size * sizeof(*labels)); - unitv = malloc(unitv_size * sizeof(*unitv)); - thlds = malloc(thlds_size * sizeof(*thlds)); - response_value = malloc(response_size * sizeof(*response_value)); - previous_value = malloc(previous_size * sizeof(*previous_value)); - eval_method = calloc(eval_size, sizeof(*eval_method)); - oids = calloc(oids_size, sizeof(char *)); - - label = strdup("SNMP"); - units = strdup(""); - port = strdup(DEFAULT_PORT); - outbuff = strdup(""); - delimiter = strdup(" = "); - output_delim = strdup(DEFAULT_OUTPUT_DELIMITER); timeout_interval = DEFAULT_SOCKET_TIMEOUT; - retries = DEFAULT_RETRIES; np_init((char *)progname, argc, argv); @@ -256,492 +105,416 @@ int main(int argc, char **argv) { np_set_args(argc, argv); + // Initialize net-snmp before touching the sessio we are going to use + init_snmp("check_snmp"); + + time_t current_time; time(¤t_time); - if (process_arguments(argc, argv) == ERROR) + process_arguments_wrapper paw_tmp = process_arguments(argc, argv); + if (paw_tmp.errorcode == ERROR) { usage4(_("Could not parse arguments")); - - if (calculate_rate) { - if (!strcmp(label, "SNMP")) - label = strdup("SNMP RATE"); - - size_t i = 0; - - previous_state = np_state_read(); - if (previous_state != NULL) { - /* Split colon separated values */ - previous_string = strdup((char *)previous_state->data); - while ((ap = strsep(&previous_string, ":")) != NULL) { - if (verbose > 2) - printf("State for %zd=%s\n", i, ap); - while (i >= previous_size) { - previous_size += OID_COUNT_STEP; - previous_value = realloc(previous_value, previous_size * sizeof(*previous_value)); - } - previous_value[i++] = strtod(ap, NULL); - } - } } - /* Populate the thresholds */ - th_warn = warning_thresholds; - th_crit = critical_thresholds; - for (size_t i = 0; i < numoids; i++) { - char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL; - char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL; - /* translate "2:1" to "@1:2" for backwards compatibility */ - w = w ? fix_snmp_range(w) : NULL; - c = c ? fix_snmp_range(c) : NULL; - - while (i >= thlds_size) { - thlds_size += OID_COUNT_STEP; - thlds = realloc(thlds, thlds_size * sizeof(*thlds)); - } + check_snmp_config config = paw_tmp.config; - /* Skip empty thresholds, while avoiding segfault */ - set_thresholds(&thlds[i], w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL, c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL); - if (w) { - th_warn = strchr(th_warn, ','); - if (th_warn) - th_warn++; - free(w); - } - if (c) { - th_crit = strchr(th_crit, ','); - if (th_crit) - th_crit++; - free(c); + if (config.ignore_mib_parsing_errors) { + char *opt_toggle_res = snmp_mib_toggle_options("e"); + if (opt_toggle_res != NULL) { + die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); } } - /* Create the command array to execute */ - if (usesnmpgetnext) { - snmpcmd = strdup(PATH_TO_SNMPGETNEXT); + struct snmp_pdu *pdu = NULL; + if (config.use_getnext) { + pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); } else { - snmpcmd = strdup(PATH_TO_SNMPGET); + pdu = snmp_pdu_create(SNMP_MSG_GET); } - /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */ - - unsigned index = 0; - command_line = calloc(11 + numcontext + numauthpriv + 1 + numoids + 1, sizeof(char *)); - - command_line[index++] = snmpcmd; - command_line[index++] = strdup("-Le"); - command_line[index++] = strdup("-t"); - xasprintf(&command_line[index++], "%d", timeout_interval); - command_line[index++] = strdup("-r"); - xasprintf(&command_line[index++], "%d", retries); - command_line[index++] = strdup("-m"); - command_line[index++] = strdup(miblist); - command_line[index++] = "-v"; - command_line[index++] = strdup(proto); - - xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s", snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", - proto); - - if (ignore_mib_parsing_errors) { - command_line[index++] = "-Pe"; - xasprintf(&cl_hidden_auth, "%s -Pe", cl_hidden_auth); - } + for (size_t i = 0; i < config.num_of_test_units; i++) { + assert(config.test_units[i].oid != NULL); + if (verbose > 0) { + printf("OID %zu to parse: %s\n", i, config.test_units[i].oid); + } - for (int i = 0; i < numcontext; i++) { - command_line[index++] = contextargs[i]; + oid tmp_OID[MAX_OID_LEN]; + size_t tmp_OID_len = MAX_OID_LEN; + if (snmp_parse_oid(config.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { + // success + snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); + } else { + // failed + snmp_perror("Parsing failure"); + die(STATE_UNKNOWN, "Failed to parse OID\n"); + } } - for (int i = 0; i < numauthpriv; i++) { - command_line[index++] = authpriv[i]; + /* Set signal handling and alarm */ + if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { + usage4(_("Cannot catch SIGALRM")); } - xasprintf(&command_line[index++], "%s:%s", server_address, port); - - xasprintf(&cl_hidden_auth, "%s [context] [authpriv] %s:%s", cl_hidden_auth, server_address, port); + const int timeout_safety_tolerance = 5; + alarm(timeout_interval * config.snmp_session.retries + timeout_safety_tolerance); - for (size_t i = 0; i < numoids; i++) { - command_line[index++] = oids[i]; - xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]); + struct snmp_session *active_session = snmp_open(&config.snmp_session); + if (active_session == NULL) { + snmp_sess_perror("Failed to open session", &config.snmp_session); + die(STATE_UNKNOWN, "Failed to open SNMP session\n"); } - command_line[index++] = NULL; + struct snmp_pdu *response = NULL; + int snmp_query_status = snmp_synch_response(active_session, pdu, &response); - if (verbose) { - printf("%s\n", cl_hidden_auth); + if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { + snmp_sess_perror("Failed to query", active_session); + // FAILED somehow + // TODO do some error analysis here + die(STATE_UNKNOWN, "SNMP query failed\n"); } - /* Set signal handling and alarm */ - if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { - usage4(_("Cannot catch SIGALRM")); - } - alarm(timeout_interval * retries + 5); - - /* Run the command */ - return_code = cmd_run_array(command_line, &chld_out, &chld_err, 0); + snmp_close(active_session); /* disable alarm again */ alarm(0); - /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs, - only return state unknown if return code is non zero or there is no stdout. - Do this way so that if there is stderr, will get added to output, which helps problem diagnosis - */ - if (return_code != 0) - external_error = 1; - if (chld_out.lines == 0) - external_error = 1; - if (external_error) { - if (chld_err.lines > 0) { - printf(_("External command error: %s\n"), chld_err.line[0]); - for (size_t i = 1; i < chld_err.lines; i++) { - printf("%s\n", chld_err.line[i]); - } - } else { - printf(_("External command error with no output (return code: %d)\n"), return_code); + mp_check overall = mp_check_init(); + + mp_subcheck sc_succesfull_query = mp_subcheck_init(); + xasprintf(&sc_succesfull_query.output, "SNMP query was succesful"); + sc_succesfull_query = mp_set_subcheck_state(sc_succesfull_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_succesfull_query); + + // We got the the query results, now process them + size_t loop_index = 0; + for (netsnmp_variable_list *vars = response->variables; vars; + vars = vars->next_variable, loop_index++) { + mp_subcheck sc_oid_test = mp_subcheck_init(); + + if (verbose > 0) { + printf("loop_index: %zu\n", loop_index); } - exit(STATE_UNKNOWN); - } - if (verbose) { - for (size_t i = 0; i < chld_out.lines; i++) { - printf("%s\n", chld_out.line[i]); + if ((config.test_units[loop_index].label != NULL) && + (strcmp(config.test_units[loop_index].label, "") != 0)) { + xasprintf(&sc_oid_test.output, "%s - ", config.test_units[loop_index].label); + } else { + sc_oid_test.output = strdup(""); } - } - line = 0; - total_oids = 0; - for (size_t i = 0; line < chld_out.lines && i < numoids; line++, i++, total_oids++) { - if (calculate_rate) - conv = "%.10g"; - else - conv = "%.0f"; - - ptr = chld_out.line[line]; - oidname = strpcpy(oidname, ptr, delimiter); - response = strstr(ptr, delimiter); - if (response == NULL) - break; + char oid_string[(MAX_OID_LEN * 2) + 1]; + memset(oid_string, 0, (MAX_OID_LEN * 2) + 1); + + int oid_string_result = + snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length); + if (oid_string_result <= 0) { + // TODO error here + } if (verbose > 2) { - printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i + 1, line + 1, oidname, response); + printf("Processing oid %s\n", oid_string); } - /* Clean up type array - Sol10 does not necessarily zero it out */ - bzero(type, sizeof(type)); - - is_counter = 0; - /* We strip out the datatype indicator for PHBs */ - if (strstr(response, "Gauge: ")) { - show = multiply(strstr(response, "Gauge: ") + 7); - } else if (strstr(response, "Gauge32: ")) { - show = multiply(strstr(response, "Gauge32: ") + 9); - } else if (strstr(response, "Counter32: ")) { - show = strstr(response, "Counter32: ") + 11; - is_counter = 1; - if (!calculate_rate) - strcpy(type, "c"); - } else if (strstr(response, "Counter64: ")) { - show = strstr(response, "Counter64: ") + 11; - is_counter = 1; - if (!calculate_rate) - strcpy(type, "c"); - } else if (strstr(response, "INTEGER: ")) { - show = multiply(strstr(response, "INTEGER: ") + 9); - - if (fmtstr_set) { - conv = fmtstr; + mp_perfdata_value pd_result_val = {0}; + xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); + sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); + + switch (vars->type) { + case ASN_OCTET_STR: { + if (verbose) { + printf("Debug: Got a string\n"); } - } else if (strstr(response, "OID: ")) { - show = strstr(response, "OID: ") + 5; - } else if (strstr(response, "STRING: ")) { - show = strstr(response, "STRING: ") + 8; - conv = "%.10g"; - - /* Get the rest of the string on multi-line strings */ - ptr = show; - COUNT_SEQ(ptr, bk_count, dq_count) - while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') { - ptr++; - GOBBLE_TOS(ptr, "\n\"\\") - COUNT_SEQ(ptr, bk_count, dq_count) + char *tmp = (char *)vars->val.string; + xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + + if (strlen(tmp) == 0) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result); } - if (dq_count) { /* unfinished line */ - /* copy show verbatim first */ - if (!mult_resp) - mult_resp = strdup(""); - xasprintf(&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show); - /* then strip out unmatched double-quote from single-line output */ - if (show[0] == '"') - show++; - - /* Keep reading until we match end of double-quoted string */ - for (line++; line < chld_out.lines; line++) { - ptr = chld_out.line[line]; - xasprintf(&mult_resp, "%s%s\n", mult_resp, ptr); - - COUNT_SEQ(ptr, bk_count, dq_count) - while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') { - ptr++; - GOBBLE_TOS(ptr, "\n\"\\") - COUNT_SEQ(ptr, bk_count, dq_count) - } - /* Break for loop before next line increment when done */ - if (!dq_count) - break; + // String matching test + if ((config.test_units[loop_index].eval_mthd.crit_string)) { + if (strcmp(tmp, config.string_cmp_value)) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (config.invert_search) ? STATE_CRITICAL : STATE_OK); + } else { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); + } + } else if (config.test_units[loop_index].eval_mthd.crit_regex) { + const int nmatch = config.regex_cmp_value.re_nsub + 1; + regmatch_t pmatch[nmatch]; + memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); + + int excode = regexec(&config.regex_cmp_value, tmp, nmatch, pmatch, 0); + if (excode == 0) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); + } else if (excode != REG_NOMATCH) { + char errbuf[MAX_INPUT_BUFFER] = ""; + regerror(excode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + printf(_("Execute Error: %s\n"), errbuf); + exit(STATE_CRITICAL); + } else { // REG_NOMATCH + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, config.invert_search ? STATE_CRITICAL : STATE_OK); } } - } else if (strstr(response, "Timeticks: ")) { - show = strstr(response, "Timeticks: "); - } else - show = response + 3; - - iresult = STATE_DEPENDENT; + mp_add_subcheck_to_check(&overall, sc_oid_test); + } + continue; + case ASN_OPAQUE: + if (verbose) { + printf("Debug: Got OPAQUE\n"); + } + break; + case ASN_COUNTER64: { + if (verbose) { + printf("Debug: Got counter64\n"); + } + struct counter64 tmp = *(vars->val.counter64); + uint64_t counter = (tmp.high << 32) + tmp.low; + counter *= config.multiplier; + counter += config.offset; + pd_result_val = mp_create_pd_value(counter); + } break; + /* Numerical values */ + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + if (verbose) { + printf("Debug: Got a Integer like\n"); + } + unsigned long tmp = *(vars->val.integer); + tmp *= config.multiplier; - /* Process this block for numeric comparisons */ - /* Make some special values,like Timeticks numeric only if a threshold is defined */ - if (thlds[i]->warning || thlds[i]->critical || calculate_rate) { - if (verbose > 2) { - print_thresholds(" thresholds", thlds[i]); + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + break; + } + case ASN_INTEGER: { + if (verbose) { + printf("Debug: Got a Integer\n"); } - ptr = strpbrk(show, "-0123456789"); - if (ptr == NULL) { - if (nulloid == 3) - die(STATE_UNKNOWN, _("No valid data returned (%s)\n"), show); - else if (nulloid == 0) - die(STATE_OK, _("No valid data returned (%s)\n"), show); - else if (nulloid == 1) - die(STATE_WARNING, _("No valid data returned (%s)\n"), show); - else if (nulloid == 2) - die(STATE_CRITICAL, _("No valid data returned (%s)\n"), show); + unsigned long tmp = *(vars->val.integer); + tmp *= config.multiplier; + + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + } break; + case ASN_FLOAT: { + if (verbose) { + printf("Debug: Got a float\n"); } - while (i >= response_size) { - response_size += OID_COUNT_STEP; - response_value = realloc(response_value, response_size * sizeof(*response_value)); + float tmp = *(vars->val.floatVal); + tmp *= config.multiplier; + + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + break; + } + case ASN_DOUBLE: { + if (verbose) { + printf("Debug: Got a double\n"); } - response_value[i] = strtod(ptr, NULL) + offset; - - if (calculate_rate) { - if (previous_state != NULL) { - duration = current_time - previous_state->time; - if (duration <= 0) - die(STATE_UNKNOWN, _("Time duration between plugin calls is invalid")); - temp_double = response_value[i] - previous_value[i]; - /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */ - if (is_counter) { - if (temp_double < (double)0.0) - temp_double += (double)4294967296.0; /* 2^32 */ - if (temp_double < (double)0.0) - temp_double += (double)18446744069414584320.0; /* 2^64-2^32 */ - ; - } - /* Convert to per second, then use multiplier */ - temp_double = temp_double / duration * rate_multiplier; - iresult = get_status(temp_double, thlds[i]); - xasprintf(&show, conv, temp_double); - } - } else { - iresult = get_status(response_value[i], thlds[i]); - xasprintf(&show, conv, response_value[i]); + double tmp = *(vars->val.doubleVal); + tmp *= config.multiplier; + tmp += config.offset; + pd_result_val = mp_create_pd_value(tmp); + break; + } + case ASN_IPADDRESS: + if (verbose) { + printf("Debug: Got an IP address\n"); } + continue; + default: + if (verbose) { + printf("Debug: Got a unmatched result type: %hhu\n", vars->type); + } + // TODO: Error here? + continue; } - /* Process this block for string matching */ - else if (eval_size > i && eval_method[i] & CRIT_STRING) { - if (strcmp(show, string_value)) - iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK; - else - iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL; + // some kind of numerical value + mp_perfdata pd_num_val = { + .value = pd_result_val, + }; + + if (!config.use_perf_data_labels_from_input) { + // Use oid for perdata label + pd_num_val.label = strdup(oid_string); + // TODO strdup error checking + } else if (config.test_units[loop_index].label != NULL || + strcmp(config.test_units[loop_index].label, "") != 0) { + pd_num_val.label = config.test_units[loop_index].label; } - /* Process this block for regex matching */ - else if (eval_size > i && eval_method[i] & CRIT_REGEX) { - excode = regexec(&preg, response, 10, pmatch, eflags); - if (excode == 0) { - iresult = (invert_search == 0) ? STATE_OK : STATE_CRITICAL; - } else if (excode != REG_NOMATCH) { - regerror(excode, &preg, errbuf, MAX_INPUT_BUFFER); - printf(_("Execute Error: %s\n"), errbuf); - exit(STATE_CRITICAL); - } else { - iresult = (invert_search == 0) ? STATE_CRITICAL : STATE_OK; - } + if (config.test_units[loop_index].unit_value != NULL && + strcmp(config.test_units[loop_index].unit_value, "") != 0) { + pd_num_val.uom = config.test_units[loop_index].unit_value; } - /* Process this block for existence-nonexistence checks */ - /* TV: Should this be outside of this else block? */ - else { - if (eval_size > i && eval_method[i] & CRIT_PRESENT) - iresult = STATE_CRITICAL; - else if (eval_size > i && eval_method[i] & WARN_PRESENT) - iresult = STATE_WARNING; - else if (response && iresult == STATE_DEPENDENT) - iresult = STATE_OK; + xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, + pd_value_to_string(pd_result_val)); + + if (config.test_units[loop_index].unit_value != NULL && + strcmp(config.test_units[loop_index].unit_value, "") != 0) { + xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, + config.test_units[loop_index].unit_value); } - /* Result is the worst outcome of all the OIDs tested */ - result = max_state(result, iresult); - - /* Prepend a label for this OID if there is one */ - if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) - xasprintf(&outbuff, "%s%s%s %s%s%s", outbuff, (i == 0) ? " " : output_delim, labels[i], mark(iresult), show, mark(iresult)); - else - xasprintf(&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim, mark(iresult), show, mark(iresult)); - - /* Append a unit string for this OID if there is one */ - if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL) - xasprintf(&outbuff, "%s %s", outbuff, unitv[i]); - - /* Write perfdata with whatever can be parsed by strtod, if possible */ - ptr = NULL; - strtod(show, &ptr); - if (ptr > show) { - if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL) - temp_string = labels[i]; - else - temp_string = oidname; - if (strpbrk(temp_string, " ='\"") == NULL) { - strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); - } else { - if (strpbrk(temp_string, "'") == NULL) { - quote_string = "'"; - } else { - quote_string = "\""; - } - strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); - strncat(perfstr, temp_string, sizeof(perfstr) - strlen(perfstr) - 1); - strncat(perfstr, quote_string, sizeof(perfstr) - strlen(perfstr) - 1); + if (config.thresholds.warning_is_set || config.thresholds.critical_is_set) { + pd_num_val = mp_pd_set_thresholds(pd_num_val, config.thresholds); + mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); + + if (tmp_state == STATE_WARNING) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); + xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", + sc_oid_test.output); + } else if (tmp_state == STATE_CRITICAL) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); + xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", + sc_oid_test.output); } - strncat(perfstr, "=", sizeof(perfstr) - strlen(perfstr) - 1); - len = sizeof(perfstr) - strlen(perfstr) - 1; - strncat(perfstr, show, len > ptr - show ? ptr - show : len); + } - if (strcmp(type, "") != 0) { - strncat(perfstr, type, sizeof(perfstr) - strlen(perfstr) - 1); - } + mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); - if (warning_thresholds) { - strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); - if (thlds[i]->warning && thlds[i]->warning->text) - strncat(perfstr, thlds[i]->warning->text, sizeof(perfstr) - strlen(perfstr) - 1); - } + mp_add_subcheck_to_check(&overall, sc_oid_test); + } - if (critical_thresholds) { - if (!warning_thresholds) - strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); - strncat(perfstr, ";", sizeof(perfstr) - strlen(perfstr) - 1); - if (thlds[i]->critical && thlds[i]->critical->text) - strncat(perfstr, thlds[i]->critical->text, sizeof(perfstr) - strlen(perfstr) - 1); - } + mp_exit(overall); +} - strncat(perfstr, " ", sizeof(perfstr) - strlen(perfstr) - 1); - } +/* process command-line arguments */ +static process_arguments_wrapper process_arguments(int argc, char **argv) { + static struct option longopts[] = { + STD_LONG_OPTS, + {"community", required_argument, 0, 'C'}, + {"oid", required_argument, 0, 'o'}, + {"object", required_argument, 0, 'o'}, + {"delimiter", required_argument, 0, 'd'}, + {"nulloid", required_argument, 0, 'z'}, + {"output-delimiter", required_argument, 0, 'D'}, + {"string", required_argument, 0, 's'}, + {"timeout", required_argument, 0, 't'}, + {"regex", required_argument, 0, 'r'}, + {"ereg", required_argument, 0, 'r'}, + {"eregi", required_argument, 0, 'R'}, + {"label", required_argument, 0, 'l'}, + {"units", required_argument, 0, 'u'}, + {"port", required_argument, 0, 'p'}, + {"retries", required_argument, 0, 'e'}, + {"miblist", required_argument, 0, 'm'}, + {"protocol", required_argument, 0, 'P'}, + {"context", required_argument, 0, 'N'}, + {"seclevel", required_argument, 0, 'L'}, + {"secname", required_argument, 0, 'U'}, + {"authproto", required_argument, 0, 'a'}, + {"privproto", required_argument, 0, 'x'}, + {"authpasswd", required_argument, 0, 'A'}, + {"privpasswd", required_argument, 0, 'X'}, + {"next", no_argument, 0, 'n'}, + {"offset", required_argument, 0, L_OFFSET}, + {"invert-search", no_argument, 0, L_INVERT_SEARCH}, + {"perf-oids", no_argument, 0, 'O'}, + {"ipv4", no_argument, 0, '4'}, + {"ipv6", no_argument, 0, '6'}, + {"multiplier", required_argument, 0, 'M'}, + {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, + {"connection-prefix", required_argument, 0, L_CONNECTION_PREFIX}, + {0, 0, 0, 0}}; + + if (argc < 2) { + process_arguments_wrapper result = { + .errorcode = ERROR, + }; + return result; } - /* Save state data, as all data collected now */ - if (calculate_rate) { - string_length = 1024; - state_string = malloc(string_length); - if (state_string == NULL) - die(STATE_UNKNOWN, _("Cannot malloc")); - - current_length = 0; - for (int i = 0; i < total_oids; i++) { - xasprintf(&temp_string, "%.0f", response_value[i]); - if (temp_string == NULL) - die(STATE_UNKNOWN, _("Cannot asprintf()")); - response_length = strlen(temp_string); - if (current_length + response_length > string_length) { - string_length = current_length + 1024; - state_string = realloc(state_string, string_length); - if (state_string == NULL) - die(STATE_UNKNOWN, _("Cannot realloc()")); + // Count number of OIDs here first + int option = 0; + size_t oid_counter = 0; + while (true) { + int option_char = getopt_long( + argc, argv, + "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); + + if (option_char == -1 || option_char == EOF) { + break; + } + + switch (option_char) { + case 'o': { + // we are going to parse this again, so we work on a copy of that string + char *tmp_oids = strdup(optarg); + if (tmp_oids == NULL) { + die(STATE_UNKNOWN, "strdup failed"); } - strcpy(&state_string[current_length], temp_string); - current_length = current_length + response_length; - state_string[current_length] = ':'; - current_length++; - free(temp_string); + + for (char *ptr = strtok(tmp_oids, ", "); ptr != NULL; + ptr = strtok(NULL, ", "), oid_counter++) { + } + break; } - state_string[--current_length] = '\0'; - if (verbose > 2) - printf("State string=%s\n", state_string); - - /* This is not strictly the same as time now, but any subtle variations will cancel out */ - np_state_write_string(current_time, state_string); - if (previous_state == NULL) { - /* Or should this be highest state? */ - die(STATE_OK, _("No previous data to calculate rate - assume okay")); + case '?': /* usage */ + usage5(); + // fallthrough + case 'h': /* help */ + print_help(); + exit(STATE_UNKNOWN); + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + + default: + continue; } } - printf("%s %s -%s %s\n", label, state_text(result), outbuff, perfstr); - if (mult_resp) - printf("%s", mult_resp); + /* Check whether at least one OID was given */ + if (oid_counter == 0) { + die(STATE_UNKNOWN, _("No OIDs specified\n")); + } - return result; -} + // Allocate space for test units + check_snmp_test_unit *tmp = calloc(oid_counter, sizeof(check_snmp_test_unit)); + if (tmp == NULL) { + die(STATE_UNKNOWN, "Failed to calloc"); + } -/* process command-line arguments */ -int process_arguments(int argc, char **argv) { - static struct option longopts[] = {STD_LONG_OPTS, - {"community", required_argument, 0, 'C'}, - {"oid", required_argument, 0, 'o'}, - {"object", required_argument, 0, 'o'}, - {"delimiter", required_argument, 0, 'd'}, - {"nulloid", required_argument, 0, 'z'}, - {"output-delimiter", required_argument, 0, 'D'}, - {"string", required_argument, 0, 's'}, - {"timeout", required_argument, 0, 't'}, - {"regex", required_argument, 0, 'r'}, - {"ereg", required_argument, 0, 'r'}, - {"eregi", required_argument, 0, 'R'}, - {"label", required_argument, 0, 'l'}, - {"units", required_argument, 0, 'u'}, - {"port", required_argument, 0, 'p'}, - {"retries", required_argument, 0, 'e'}, - {"miblist", required_argument, 0, 'm'}, - {"protocol", required_argument, 0, 'P'}, - {"context", required_argument, 0, 'N'}, - {"seclevel", required_argument, 0, 'L'}, - {"secname", required_argument, 0, 'U'}, - {"authproto", required_argument, 0, 'a'}, - {"privproto", required_argument, 0, 'x'}, - {"authpasswd", required_argument, 0, 'A'}, - {"privpasswd", required_argument, 0, 'X'}, - {"next", no_argument, 0, 'n'}, - {"rate", no_argument, 0, L_CALCULATE_RATE}, - {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER}, - {"offset", required_argument, 0, L_OFFSET}, - {"invert-search", no_argument, 0, L_INVERT_SEARCH}, - {"perf-oids", no_argument, 0, 'O'}, - {"ipv4", no_argument, 0, '4'}, - {"ipv6", no_argument, 0, '6'}, - {"multiplier", required_argument, 0, 'M'}, - {"fmtstr", required_argument, 0, 'f'}, - {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, - {0, 0, 0, 0}}; - - if (argc < 2) - return ERROR; - - /* reverse compatibility for very old non-POSIX usage forms */ - for (int c = 1; c < argc; c++) { - if (strcmp("-to", argv[c]) == 0) - strcpy(argv[c], "-t"); - if (strcmp("-wv", argv[c]) == 0) - strcpy(argv[c], "-w"); - if (strcmp("-cv", argv[c]) == 0) - strcpy(argv[c], "-c"); + for (size_t i = 0; i < oid_counter; i++) { + tmp[i] = check_snmp_test_unit_init(); } - size_t j = 0; - size_t jj = 0; + check_snmp_config config = check_snmp_config_init(); + config.test_units = tmp; + config.num_of_test_units = oid_counter; + + option = 0; + optind = 1; // Reset argument scanner + size_t tmp_oid_counter = 0; + size_t eval_counter = 0; + size_t unitv_counter = 0; + size_t labels_counter = 0; + unsigned char *authpasswd = NULL; + unsigned char *privpasswd = NULL; + int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE; + char *port = NULL; + char *miblist = NULL; + char *connection_prefix = NULL; + // TODO error checking while (true) { - int option = 0; - int option_char = getopt_long(argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); + int option_char = getopt_long( + argc, argv, + "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:M:f:z:", longopts, &option); - if (option_char == -1 || option_char == EOF) + if (option_char == -1 || option_char == EOF) { break; + } switch (option_char) { case '?': /* usage */ @@ -758,65 +531,152 @@ int process_arguments(int argc, char **argv) { /* Connection info */ case 'C': /* group or community */ - community = optarg; + config.snmp_session.community = (unsigned char *)optarg; + config.snmp_session.community_len = strlen(optarg); break; case 'H': /* Host or server */ - server_address = optarg; + config.snmp_session.peername = optarg; break; - case 'p': /* TCP port number */ + case 'p': /*port number */ + // Add port to "peername" below to not rely on argument order port = optarg; break; case 'm': /* List of MIBS */ miblist = optarg; break; - case 'n': /* usesnmpgetnext */ - usesnmpgetnext = true; + case 'n': /* use_getnext instead of get */ + config.use_getnext = true; break; case 'P': /* SNMP protocol version */ - proto = optarg; + if (strcasecmp("1", optarg) == 0) { + config.snmp_session.version = SNMP_VERSION_1; + } else if (strcasecmp("2c", optarg) == 0) { + config.snmp_session.version = SNMP_VERSION_2c; + } else if (strcasecmp("3", optarg) == 0) { + config.snmp_session.version = SNMP_VERSION_3; + } else { + die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); + } break; - case 'N': /* SNMPv3 context */ - context = optarg; + case 'N': /* SNMPv3 context name */ + config.snmp_session.contextName = optarg; + config.snmp_session.contextNameLen = strlen(optarg); break; case 'L': /* security level */ - seclevel = optarg; + if (strcasecmp("noAuthNoPriv", optarg) == 0) { + config.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + } else if (strcasecmp("authNoPriv", optarg) == 0) { + config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + } else if (strcasecmp("authPriv", optarg) == 0) { + config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; + } else { + die(STATE_UNKNOWN, "invalid security level: %s", optarg); + } break; case 'U': /* security username */ - secname = optarg; + config.snmp_session.securityName = optarg; + config.snmp_session.securityNameLen = strlen(optarg); break; case 'a': /* auth protocol */ - authproto = optarg; + // SNMPv3: SHA or MD5 + // TODO Test for availability of individual protocols + if (strcasecmp("MD5", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol); + } else if (strcasecmp("SHA", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol); + } else if (strcasecmp("SHA224", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); + } else if (strcasecmp("SHA256", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); + } else if (strcasecmp("SHA384", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol); + } else if (strcasecmp("SHA512", optarg) == 0) { + config.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol); + } else { + die(STATE_UNKNOWN, "Unknown authentication protocol"); + } break; case 'x': /* priv protocol */ - privproto = optarg; + if (strcasecmp("DES", optarg) == 0) { + config.snmp_session.securityAuthProto = usmDESPrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); + } else if (strcasecmp("AES", optarg) == 0) { + config.snmp_session.securityAuthProto = usmAESPrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); + // } else if (strcasecmp("AES128", optarg)) { + // config.snmp_session.securityAuthProto = usmAES128PrivProtocol; + // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol) + // / OID_LENGTH(oid); + } else if (strcasecmp("AES192", optarg) == 0) { + config.snmp_session.securityAuthProto = usmAES192PrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES192PrivProtocol); + } else if (strcasecmp("AES256", optarg) == 0) { + config.snmp_session.securityAuthProto = usmAES256PrivProtocol; + config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES256PrivProtocol); + // } else if (strcasecmp("AES192Cisco", optarg)) { + // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol; + // config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); } else if + // (strcasecmp("AES256Cisco", optarg)) { config.snmp_session.securityAuthProto = + // usmAES256CiscoPrivProtocol; config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); } else if + // (strcasecmp("AES192Cisco2", optarg)) { config.snmp_session.securityAuthProto + // = usmAES192Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES192Cisco2PrivProtocol) / sizeof(oid); } else if + // (strcasecmp("AES256Cisco2", optarg)) { config.snmp_session.securityAuthProto + // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = + // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid); + } else { + die(STATE_UNKNOWN, "Unknow privacy protocol"); + } break; case 'A': /* auth passwd */ - authpasswd = optarg; + authpasswd = (unsigned char *)optarg; break; case 'X': /* priv passwd */ - privpasswd = optarg; + privpasswd = (unsigned char *)optarg; + break; + case 'e': + case 'E': + if (!is_integer(optarg)) { + usage2(_("Retries interval must be a positive integer"), optarg); + } else { + config.snmp_session.retries = atoi(optarg); + } break; case 't': /* timeout period */ - if (!is_integer(optarg)) + if (!is_integer(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); - else + } else { timeout_interval = atoi(optarg); + } break; /* Test parameters */ case 'c': /* critical threshold */ - critical_thresholds = optarg; - break; + { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", optarg); + } + config.thresholds.critical = tmp.range; + config.thresholds.critical_is_set = true; + } break; case 'w': /* warning threshold */ - warning_thresholds = optarg; - break; - case 'e': /* PRELIMINARY - may change */ - case 'E': /* PRELIMINARY - may change */ - if (!is_integer(optarg)) - usage2(_("Retries interval must be a positive integer"), optarg); - else - retries = atoi(optarg); - break; + { + mp_range_parsed tmp = mp_parse_range_string(optarg); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse warning threshold range: %s", optarg); + } + config.thresholds.warning = tmp.range; + config.thresholds.warning_is_set = true; + } break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { /* @@ -824,306 +684,246 @@ int process_arguments(int argc, char **argv) { * so we have a mib variable, rather than just an SNMP OID, * so we have to actually read the mib files */ - needmibs = true; - } - for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) { - while (j >= oids_size) { - oids_size += OID_COUNT_STEP; - oids = realloc(oids, oids_size * sizeof(*oids)); - } - oids[j] = strdup(ptr); + config.need_mibs = true; } - numoids = j; - if (option_char == 'E' || option_char == 'e') { - jj++; - while (j + 1 >= eval_size) { - eval_size += OID_COUNT_STEP; - eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); - memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); - } - if (option_char == 'E') - eval_method[j + 1] |= WARN_PRESENT; - else if (option_char == 'e') - eval_method[j + 1] |= CRIT_PRESENT; + + for (char *ptr = strtok(optarg, ", "); ptr != NULL; + ptr = strtok(NULL, ", "), tmp_oid_counter++) { + config.test_units[tmp_oid_counter].oid = strdup(ptr); } break; case 'z': /* Null OID Return Check */ - if (!is_integer(optarg)) + if (!is_integer(optarg)) { usage2(_("Exit status must be a positive integer"), optarg); - else - nulloid = atoi(optarg); + } else { + config.nulloid_result = atoi(optarg); + } break; case 's': /* string or substring */ - strncpy(string_value, optarg, sizeof(string_value) - 1); - string_value[sizeof(string_value) - 1] = 0; - while (jj >= eval_size) { - eval_size += OID_COUNT_STEP; - eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); - memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); - } - eval_method[jj++] = CRIT_STRING; + strncpy(config.string_cmp_value, optarg, sizeof(config.string_cmp_value) - 1); + config.string_cmp_value[sizeof(config.string_cmp_value) - 1] = 0; + config.test_units[eval_counter++].eval_mthd.crit_string = true; break; case 'R': /* regex */ cflags = REG_ICASE; // fall through case 'r': /* regex */ + { + char regex_expect[MAX_INPUT_BUFFER] = ""; cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); regex_expect[sizeof(regex_expect) - 1] = 0; - errcode = regcomp(&preg, regex_expect, cflags); + int errcode = regcomp(&config.regex_cmp_value, regex_expect, cflags); if (errcode != 0) { - regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); - printf(_("Could Not Compile Regular Expression")); - return ERROR; + char errbuf[MAX_INPUT_BUFFER] = ""; + regerror(errcode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + printf("Could Not Compile Regular Expression: %s", errbuf); + process_arguments_wrapper result = { + .errorcode = ERROR, + }; + return result; } - while (jj >= eval_size) { - eval_size += OID_COUNT_STEP; - eval_method = realloc(eval_method, eval_size * sizeof(*eval_method)); - memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8); - } - eval_method[jj++] = CRIT_REGEX; - break; - - /* Format */ - case 'd': /* delimiter */ - delimiter = strscpy(delimiter, optarg); - break; - case 'D': /* output-delimiter */ - output_delim = strscpy(output_delim, optarg); - break; + config.test_units[eval_counter++].eval_mthd.crit_regex = true; + } break; case 'l': /* label */ - nlabels++; - if (nlabels > labels_size) { - labels_size += 8; - labels = realloc(labels, labels_size * sizeof(*labels)); - if (labels == NULL) - die(STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels); + { + if (labels_counter >= config.num_of_test_units) { + break; } - labels[nlabels - 1] = optarg; - char *ptr = thisarg(optarg); - labels[nlabels - 1] = ptr; - if (ptr[0] == '\'') - labels[nlabels - 1] = ptr + 1; - while (ptr && (ptr = nextarg(ptr))) { - nlabels++; - if (nlabels > labels_size) { - labels_size += 8; - labels = realloc(labels, labels_size * sizeof(*labels)); - if (labels == NULL) - die(STATE_UNKNOWN, _("Could not reallocate labels\n")); + char *ptr = trim_whitespaces_and_check_quoting(optarg); + if (ptr[0] == '\'') { + config.test_units[labels_counter].label = ptr + 1; + } else { + config.test_units[labels_counter].label = ptr; + } + + while (ptr && (ptr = get_next_argument(ptr))) { + labels_counter++; + ptr = trim_whitespaces_and_check_quoting(ptr); + if (ptr[0] == '\'') { + config.test_units[labels_counter].label = ptr + 1; + } else { + config.test_units[labels_counter].label = ptr; } - ptr = thisarg(ptr); - if (ptr[0] == '\'') - labels[nlabels - 1] = ptr + 1; - else - labels[nlabels - 1] = ptr; } - break; + labels_counter++; + } break; case 'u': /* units */ - units = optarg; - nunits++; - if (nunits > unitv_size) { - unitv_size += 8; - unitv = realloc(unitv, unitv_size * sizeof(*unitv)); - if (unitv == NULL) - die(STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits); + { + if (unitv_counter >= config.num_of_test_units) { + break; } - unitv[nunits - 1] = optarg; - ptr = thisarg(optarg); - unitv[nunits - 1] = ptr; - if (ptr[0] == '\'') - unitv[nunits - 1] = ptr + 1; - while (ptr && (ptr = nextarg(ptr))) { - if (nunits > unitv_size) { - unitv_size += 8; - unitv = realloc(unitv, unitv_size * sizeof(*unitv)); - if (units == NULL) - die(STATE_UNKNOWN, _("Could not realloc() units\n")); + char *ptr = trim_whitespaces_and_check_quoting(optarg); + if (ptr[0] == '\'') { + config.test_units[unitv_counter].unit_value = ptr + 1; + } else { + config.test_units[unitv_counter].unit_value = ptr; + } + while (ptr && (ptr = get_next_argument(ptr))) { + unitv_counter++; + ptr = trim_whitespaces_and_check_quoting(ptr); + if (ptr[0] == '\'') { + config.test_units[unitv_counter].unit_value = ptr + 1; + } else { + config.test_units[unitv_counter].unit_value = ptr; } - nunits++; - ptr = thisarg(ptr); - if (ptr[0] == '\'') - unitv[nunits - 1] = ptr + 1; - else - unitv[nunits - 1] = ptr; } - break; - case L_CALCULATE_RATE: - if (calculate_rate == 0) - np_enable_state(NULL, 1); - calculate_rate = 1; - break; - case L_RATE_MULTIPLIER: - if (!is_integer(optarg) || ((rate_multiplier = atoi(optarg)) <= 0)) - usage2(_("Rate multiplier must be a positive integer"), optarg); - break; + unitv_counter++; + } break; case L_OFFSET: - offset = strtod(optarg, NULL); + config.offset = strtod(optarg, NULL); break; case L_INVERT_SEARCH: - invert_search = 1; + config.invert_search = false; break; case 'O': - perf_labels = 0; + config.use_perf_data_labels_from_input = true; break; case '4': + // The default, do something here to be exclusive to -6 instead of doing nothing? + connection_prefix = "udp"; break; case '6': - xasprintf(&ip_version, "udp6:"); - if (verbose > 2) - printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n"); + connection_prefix = "udp6"; + break; + case L_CONNECTION_PREFIX: + connection_prefix = optarg; break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { - multiplier = strtod(optarg, NULL); - } - break; - case 'f': - if (multiplier != 1.0) { - fmtstr = optarg; - fmtstr_set = true; + config.multiplier = strtod(optarg, NULL); } break; case L_IGNORE_MIB_PARSING_ERRORS: - ignore_mib_parsing_errors = true; + config.ignore_mib_parsing_errors = true; } } - if (server_address == NULL) - server_address = argv[optind]; - - if (community == NULL) - community = strdup(DEFAULT_COMMUNITY); - - return validate_arguments(); -} - -/****************************************************************************** - -@@- - -validate_arguments - -&PROTO_validate_arguments; - -Checks to see if the default miblist needs to be loaded. Also verifies -the authentication and authorization combinations based on protocol version -selected. - - - - --@@ -******************************************************************************/ + if (config.snmp_session.peername == NULL) { + config.snmp_session.peername = argv[optind]; + } -static int validate_arguments() { - /* check whether to load locally installed MIBS (CPU/disk intensive) */ - if (miblist == NULL) { - if (needmibs) { - miblist = strdup(DEFAULT_MIBLIST); + // Build true peername here if necessary + if (connection_prefix != NULL) { + // We got something in the connection prefix + if (strcasecmp(connection_prefix, "udp") == 0) { + // The default, do nothing + } else if (strcasecmp(connection_prefix, "tcp") == 0) { + // use tcp/ipv4 + xasprintf(&config.snmp_session.peername, "tcp:%s", config.snmp_session.peername); + } else if (strcasecmp(connection_prefix, "tcp6") == 0 || + strcasecmp(connection_prefix, "tcpv6") == 0 || + strcasecmp(connection_prefix, "tcpipv6") == 0 || + strcasecmp(connection_prefix, "udp6") == 0 || + strcasecmp(connection_prefix, "udpipv6") == 0 || + strcasecmp(connection_prefix, "udpv6") == 0) { + // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it + // works anyway therefore do nothing here + xasprintf(&config.snmp_session.peername, "%s:%s", connection_prefix, + config.snmp_session.peername); + } else if (strcmp(connection_prefix, "tls") == 0) { + // TODO: Anything else to do here? + xasprintf(&config.snmp_session.peername, "tls:%s", config.snmp_session.peername); + } else if (strcmp(connection_prefix, "dtls") == 0) { + // TODO: Anything else to do here? + xasprintf(&config.snmp_session.peername, "dtls:%s", config.snmp_session.peername); + } else if (strcmp(connection_prefix, "unix") == 0) { + // TODO: Check whether this is a valid path? + xasprintf(&config.snmp_session.peername, "unix:%s", config.snmp_session.peername); + } else if (strcmp(connection_prefix, "ipx") == 0) { + xasprintf(&config.snmp_session.peername, "ipx:%s", config.snmp_session.peername); } else { - miblist = ""; /* don't read any mib files for numeric oids */ + // Don't know that prefix, die here + die(STATE_UNKNOWN, "Unknown connection prefix"); } } /* Check server_address is given */ - if (server_address == NULL) + if (config.snmp_session.peername == NULL) { die(STATE_UNKNOWN, _("No host specified\n")); + } - /* Check oid is given */ - if (numoids == 0) - die(STATE_UNKNOWN, _("No OIDs specified\n")); + if (port != NULL) { + xasprintf(&config.snmp_session.peername, "%s:%s", config.snmp_session.peername, port); + } - if (proto == NULL) - xasprintf(&proto, DEFAULT_PROTOCOL); - - if ((strcmp(proto, "1") == 0) || (strcmp(proto, "2c") == 0)) { /* snmpv1 or snmpv2c */ - numauthpriv = 2; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-c"); - authpriv[1] = strdup(community); - } else if (strcmp(proto, "3") == 0) { /* snmpv3 args */ - if (!(context == NULL)) { - numcontext = 2; - contextargs = calloc(numcontext, sizeof(char *)); - contextargs[0] = strdup("-n"); - contextargs[1] = strdup(context); + /* check whether to load locally installed MIBS (CPU/disk intensive) */ + if (miblist == NULL) { + if (config.need_mibs) { + setenv("MIBLS", DEFAULT_MIBLIST, 1); + } else { + setenv("MIBLS", "NONE", 1); + miblist = ""; /* don't read any mib files for numeric oids */ } + } else { + // Blatantly stolen from snmplib/snmp_parse_args + setenv("MIBS", miblist, 1); + } - if (seclevel == NULL) - xasprintf(&seclevel, "noAuthNoPriv"); - - if (secname == NULL) + if ((config.snmp_session.version == SNMP_VERSION_1) || + (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ + /* + config.numauthpriv = 2; + config.authpriv = calloc(config.numauthpriv, sizeof(char *)); + config.authpriv[0] = strdup("-c"); + config.authpriv[1] = strdup(community); + */ + } else if (config.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ + // generate keys for priv and auth here (if demanded) + + if (config.snmp_session.securityName == NULL) { die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); + } - if (strcmp(seclevel, "noAuthNoPriv") == 0) { - numauthpriv = 4; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-l"); - authpriv[1] = strdup("noAuthNoPriv"); - authpriv[2] = strdup("-u"); - authpriv[3] = strdup(secname); - } else { - if (!((strcmp(seclevel, "authNoPriv") == 0) || (strcmp(seclevel, "authPriv") == 0))) { - usage2(_("Invalid seclevel"), seclevel); + switch (config.snmp_session.securityLevel) { + case SNMP_SEC_LEVEL_AUTHPRIV: { + if (authpasswd == NULL) { + die(STATE_UNKNOWN, + "No authentication passphrase was given, but authorization was requested"); } - - if (authproto == NULL) - xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL); - - if (authpasswd == NULL) - die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd"); - - if (strcmp(seclevel, "authNoPriv") == 0) { - numauthpriv = 8; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-l"); - authpriv[1] = strdup("authNoPriv"); - authpriv[2] = strdup("-a"); - authpriv[3] = strdup(authproto); - authpriv[4] = strdup("-u"); - authpriv[5] = strdup(secname); - authpriv[6] = strdup("-A"); - authpriv[7] = strdup(authpasswd); - } else if (strcmp(seclevel, "authPriv") == 0) { - if (privproto == NULL) - xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL); - - if (privpasswd == NULL) - die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd"); - - numauthpriv = 12; - authpriv = calloc(numauthpriv, sizeof(char *)); - authpriv[0] = strdup("-l"); - authpriv[1] = strdup("authPriv"); - authpriv[2] = strdup("-a"); - authpriv[3] = strdup(authproto); - authpriv[4] = strdup("-u"); - authpriv[5] = strdup(secname); - authpriv[6] = strdup("-A"); - authpriv[7] = strdup(authpasswd); - authpriv[8] = strdup("-x"); - authpriv[9] = strdup(privproto); - authpriv[10] = strdup("-X"); - authpriv[11] = strdup(privpasswd); + // auth and priv + size_t priv_key_generated = generate_Ku( + config.snmp_session.securityPrivProto, config.snmp_session.securityPrivProtoLen, + authpasswd, strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, + &config.snmp_session.securityPrivKeyLen); + if (priv_key_generated != SNMPERR_SUCCESS) { + die(STATE_UNKNOWN, "Failed to generate privacy key"); } } - - } else { - usage2(_("Invalid SNMP version"), proto); + // fall through + case SNMP_SEC_LEVEL_AUTHNOPRIV: { + if (privpasswd == NULL) { + die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); + } + size_t auth_key_generated = generate_Ku( + config.snmp_session.securityAuthProto, config.snmp_session.securityAuthProtoLen, + privpasswd, strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, + &config.snmp_session.securityAuthKeyLen); + if (auth_key_generated != SNMPERR_SUCCESS) { + die(STATE_UNKNOWN, "Failed to generate privacy key"); + } + } break; + case SNMP_SEC_LEVEL_NOAUTH: + // No auth, no priv, not much todo + break; + } } - return OK; + process_arguments_wrapper result = { + .config = config, + .errorcode = OK, + }; + return result; } /* trim leading whitespace if there is a leading quote, make sure it balances */ - -static char *thisarg(char *str) { +char *trim_whitespaces_and_check_quoting(char *str) { str += strspn(str, " \t\r\n"); /* trim any leading whitespace */ if (str[0] == '\'') { /* handle SIMPLE quoted strings */ - if (strlen(str) == 1 || !strstr(str + 1, "'")) + if (strlen(str) == 1 || !strstr(str + 1, "'")) { die(STATE_UNKNOWN, _("Unbalanced quotes\n")); + } } return str; } @@ -1132,23 +932,21 @@ static char *thisarg(char *str) { set the trailing quote to '\x0' if the string continues, advance beyond the comma */ -static char *nextarg(char *str) { +char *get_next_argument(char *str) { if (str[0] == '\'') { str[0] = 0; if (strlen(str) > 1) { str = strstr(str + 1, "'"); return (++str); - } else { - return NULL; } + return NULL; } if (str[0] == ',') { str[0] = 0; if (strlen(str) > 1) { return (++str); - } else { - return NULL; } + return NULL; } if ((str = strstr(str, ",")) && strlen(str) > 1) { str[0] = 0; @@ -1158,40 +956,53 @@ static char *nextarg(char *str) { } /* multiply result (values 0 < n < 1 work as divider) */ -static char *multiply(char *str) { - if (multiplier == 1) +char *multiply(char *str, double multiplier, char *fmt_str) { + + if (multiplier == 1) { return (str); + } - if (verbose > 2) + if (verbose > 2) { printf(" multiply input: %s\n", str); + } char *endptr; double val = strtod(str, &endptr); if ((val == 0.0) && (endptr == str)) { - die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, str); + die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, + str); } - if (verbose > 2) + if (verbose > 2) { printf(" multiply extracted double: %f\n", val); + } val *= multiplier; char *conv = "%f"; - if (fmtstr_set) { - conv = fmtstr; + if (fmt_str != NULL) { + conv = fmt_str; } + + char *buffer = calloc(1, DEFAULT_BUFFER_SIZE); + if (buffer == NULL) { + die(STATE_UNKNOWN, "calloc failed"); + } + if (val == (int)val) { snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val); } else { - if (verbose > 2) + if (verbose > 2) { printf(" multiply using format: %s\n", conv); + } snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val); } - if (verbose > 2) + if (verbose > 2) { printf(" multiply result: %s\n", buffer); + } return buffer; } -static void print_help(void) { +void print_help(void) { print_revision(progname, NP_VERSION); printf(COPYRIGHT, copyright, email); @@ -1204,8 +1015,6 @@ static void print_help(void) { printf(UT_HELP_VRSN); printf(UT_EXTRA_OPTS); - printf(UT_IPv46); - printf(UT_HOST_PORT, 'p', DEFAULT_PORT); /* SNMP and Authentication Protocol */ @@ -1217,13 +1026,10 @@ static void print_help(void) { printf(" %s\n", _("SNMPv3 context")); printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]"); printf(" %s\n", _("SNMPv3 securityLevel")); - printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL"); - printf(" %s\n", - _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools")); - printf(" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512")); - printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL"); - printf(" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools")); - printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256")); + printf(" %s\n", "-a, --authproto=[MD5|SHA]"); + printf(" %s\n", _("SNMPv3 auth proto")); + printf(" %s\n", "-x, --privproto=[DES|AES]"); + printf(" %s\n", _("SNMPv3 priv proto (default DES)")); /* Authentication Tokens*/ printf(" %s\n", "-C, --community=STRING"); @@ -1235,15 +1041,21 @@ static void print_help(void) { printf(" %s\n", _("SNMPv3 authentication password")); printf(" %s\n", "-X, --privpasswd=PASSWORD"); printf(" %s\n", _("SNMPv3 privacy password")); + printf(" %s\n", "--connection-prefix"); + printf(" Connection prefix, may be one of udp, udp6, tcp, unix, ipx, udp6, udpv6, udpipv6, " + "tcp6, tcpv6, tcpipv6, tls, dtls - " + "default is \"udp\"\n"); /* OID Stuff */ printf(" %s\n", "-o, --oid=OID(s)"); printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query")); printf(" %s\n", "-m, --miblist=STRING"); - printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); + printf(" %s\n", + _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); printf(" %s\n", _("for symbolic OIDs.)")); printf(" %s\n", "-d, --delimiter=STRING"); - printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER); + printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), + DEFAULT_DELIMITER); printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); printf(" %s\n", _("to be the data that should be used in the evaluation.")); printf(" %s\n", "-z, --nulloid=#"); @@ -1260,10 +1072,6 @@ static void print_help(void) { printf(" %s\n", _("Warning threshold range(s)")); printf(" %s\n", "-c, --critical=THRESHOLD(s)"); printf(" %s\n", _("Critical threshold range(s)")); - printf(" %s\n", "--rate"); - printf(" %s\n", _("Enable rate calculation. See 'Rate Calculation' below")); - printf(" %s\n", "--rate-multiplier"); - printf(" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute")); printf(" %s\n", "--offset=OFFSET"); printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data")); @@ -1271,9 +1079,11 @@ static void print_help(void) { printf(" %s\n", "-s, --string=STRING"); printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match")); printf(" %s\n", "-r, --ereg=REGEX"); - printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches")); + printf(" %s\n", + _("Return OK state (for that OID) if extended regular expression REGEX matches")); printf(" %s\n", "-R, --eregi=REGEX"); - printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); + printf(" %s\n", + _("Return OK state (for that OID) if case-insensitive extended REGEX matches")); printf(" %s\n", "--invert-search"); printf(" %s\n", _("Invert search result (CRITICAL if found)")); @@ -1282,53 +1092,45 @@ static void print_help(void) { printf(" %s\n", _("Prefix label for output from plugin")); printf(" %s\n", "-u, --units=STRING"); printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); - printf(" %s\n", "-D, --output-delimiter=STRING"); - printf(" %s\n", _("Separates output on multiple OID requests")); printf(" %s\n", "-M, --multiplier=FLOAT"); printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); - printf(" %s\n", "-f, --fmtstr=STRING"); - printf(" %s\n", _("C-style format string for float values (see option -M)")); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); - printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5")); + printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: " + "timeout_interval * retries + 5")); printf(" %s\n", "-e, --retries=INTEGER"); - printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES); + printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), + DEFAULT_RETRIES); printf(" %s\n", "-O, --perf-oids"); printf(" %s\n", _("Label performance data with OIDs instead of --label's")); printf(" %s\n", "--ignore-mib-parsing-errors"); - printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files")); + printf(" %s\n", _("Do to not print errors encountered when parsing MIB files")); printf(UT_VERBOSE); printf("\n"); - printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package.")); - printf("%s\n", _("if you don't have the package installed, you will need to download it from")); + printf("%s\n", _("This plugin relies (links against) on the NET-SNMP libraries.")); + printf("%s\n", + _("if you don't have the libraries installed, you will need to download them from")); printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin.")); printf("\n"); printf("%s\n", _("Notes:")); - printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); + printf(" %s\n", + _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited ")); printf(" %s\n", _("list (lists with internal spaces must be quoted).")); printf(" -%s", UT_THRESHOLDS_NOTES); - printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); + printf(" %s\n", + _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'")); printf(" %s\n", _("- Note that only one string and one regex may be checked at present")); - printf(" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); + printf(" %s\n", + _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value")); printf(" %s\n", _("returned from the SNMP query is an unsigned integer.")); - printf("\n"); - printf("%s\n", _("Rate Calculation:")); - printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when")); - printf(" %s\n", _("calculating the counter difference since the last check. check_snmp")); - printf(" %s\n", _("saves the last state information in a file so that the rate per second")); - printf(" %s\n", _("can be calculated. Use the --rate option to save state information.")); - printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK.")); - printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so")); - printf(" %s\n", _("changing the arguments will create a new state file.")); - printf(UT_SUPPORT); } diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h new file mode 100644 index 00000000..e2e1d6c2 --- /dev/null +++ b/plugins/check_snmp.d/config.h @@ -0,0 +1,107 @@ +#pragma once + +#include "states.h" +#include "thresholds.h" +#include "utils_base.h" +#include +#include +#include + +// defines for snmp libs +#define u_char unsigned char +#define u_long unsigned long +#define u_short unsigned short +#define u_int unsigned int + +#include +#include +#include +#include + +const int DEFAULT_PROTOCOL = SNMP_VERSION_1; +const char DEFAULT_PORT[] = "161"; +const char DEFAULT_OUTPUT_DELIMITER[] = " "; +const int DEFAULT_RETRIES = 5; + +const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; + +typedef struct eval_method { + bool crit_string; + bool crit_regex; +} eval_method; + +typedef struct check_snmp_test_unit { + char *oid; + char *label; + char *unit_value; + eval_method eval_mthd; +} check_snmp_test_unit; + +check_snmp_test_unit check_snmp_test_unit_init() { + check_snmp_test_unit tmp = {}; + return tmp; +} + +typedef struct check_snmp_config { + // SNMP session to use + struct snmp_session snmp_session; + + // use getnet instead of get + bool use_getnext; + + // TODO actually make these useful + bool ignore_mib_parsing_errors; + bool need_mibs; + + check_snmp_test_unit *test_units; + size_t num_of_test_units; + mp_thresholds thresholds; + + // State if an empty value is encountered + mp_state_enum nulloid_result; + + // String evaluation stuff + bool invert_search; + regex_t regex_cmp_value; // regex to match query results against + char string_cmp_value[MAX_INPUT_BUFFER]; + + // Modify data + double multiplier; + double offset; + + // Modify output + bool use_perf_data_labels_from_input; +} check_snmp_config; + +check_snmp_config check_snmp_config_init() { + check_snmp_config tmp = { + .use_getnext = false, + + .ignore_mib_parsing_errors = false, + .need_mibs = false, + + .test_units = NULL, + .num_of_test_units = 0, + .thresholds = mp_thresholds_init(), + + .nulloid_result = STATE_UNKNOWN, // state to return if no result for query + + .invert_search = true, + .regex_cmp_value = {}, + .string_cmp_value = "", + + .multiplier = 1.0, + .offset = 0, + + .use_perf_data_labels_from_input = false, + }; + + snmp_sess_init(&tmp.snmp_session); + + tmp.snmp_session.retries = DEFAULT_RETRIES; + tmp.snmp_session.version = DEFAULT_SNMP_VERSION; + tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + tmp.snmp_session.community = (unsigned char *)"public"; + tmp.snmp_session.community_len = strlen("public"); + return tmp; +} -- cgit v1.2.3-74-g34f1 From babeb765e5725610dbf7673c91a3a5a4e5a8810f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:15:45 +0200 Subject: Fix range comparison and aesthetic improvements --- lib/utils_base.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/utils_base.c b/lib/utils_base.c index c49a473f..1e92b29b 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -222,15 +222,15 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) { bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { bool is_inside = false; - if (my_range.end_infinity == false && my_range.start_infinity == false) { + if (!my_range.end_infinity && !my_range.start_infinity) { // range: .........|---inside---|........... // value - is_inside = ((cmp_perfdata_value(my_range.start, value) < 1) && (cmp_perfdata_value(value, my_range.end) <= 0)); - } else if (my_range.start_infinity == false && my_range.end_infinity == true) { + is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) && (cmp_perfdata_value(value, my_range.end) <= 0)); + } else if (!my_range.start_infinity && my_range.end_infinity) { // range: .........|---inside--------- // value - is_inside = (cmp_perfdata_value(my_range.start, value) < 0); - } else if (my_range.start_infinity == true && my_range.end_infinity == false) { + is_inside = (cmp_perfdata_value(value, my_range.start) >= 0); + } else if (my_range.start_infinity && !my_range.end_infinity) { // range: -inside--------|.................... // value is_inside = (cmp_perfdata_value(value, my_range.end) == -1); -- cgit v1.2.3-74-g34f1 From a27862a9c774d3fc4a608f8593c83b11357cc6dc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:17:46 +0200 Subject: check_snmp: rebuild threshold parsing --- plugins/Makefile.am | 1 + plugins/check_snmp.c | 26 +++----- plugins/check_snmp.d/check_snmp_helpers.c | 103 ++++++++++++++++++++++++++++++ plugins/check_snmp.d/check_snmp_helpers.h | 8 +++ plugins/check_snmp.d/config.h | 52 ++------------- 5 files changed, 125 insertions(+), 65 deletions(-) create mode 100644 plugins/check_snmp.d/check_snmp_helpers.c create mode 100644 plugins/check_snmp.d/check_snmp_helpers.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 765e2687..38668348 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -152,6 +152,7 @@ check_ping_LDADD = $(NETLIBS) check_procs_LDADD = $(BASEOBJS) check_radius_LDADD = $(NETLIBS) $(RADIUSLIBS) check_real_LDADD = $(NETLIBS) +check_snmp_SOURCES = check_snmp.c check_snmp.d/check_snmp_helpers.c check_snmp_LDADD = $(BASEOBJS) check_snmp_LDFLAGS = $(AM_LDFLAGS) `net-snmp-config --libs` check_snmp_CFLAGS = $(AM_CFLAGS) `net-snmp-config --cflags` diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 6c9ed959..b71ee4fd 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -41,6 +41,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "../lib/utils_base.h" #include "../lib/output.h" #include "../lib/perfdata.h" +#include "check_snmp.d/check_snmp_helpers.h" #include #include @@ -366,8 +367,9 @@ int main(int argc, char **argv) { config.test_units[loop_index].unit_value); } - if (config.thresholds.warning_is_set || config.thresholds.critical_is_set) { - pd_num_val = mp_pd_set_thresholds(pd_num_val, config.thresholds); + if (config.test_units[loop_index].threshold.warning_is_set || + config.test_units[loop_index].threshold.critical_is_set) { + pd_num_val = mp_pd_set_thresholds(pd_num_val, config.test_units[loop_index].threshold); mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); if (tmp_state == STATE_WARNING) { @@ -660,23 +662,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Test parameters */ case 'c': /* critical threshold */ - { - mp_range_parsed tmp = mp_parse_range_string(optarg); - if (tmp.error != MP_PARSING_SUCCES) { - die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", optarg); - } - config.thresholds.critical = tmp.range; - config.thresholds.critical_is_set = true; - } break; + check_snmp_set_thresholds(optarg, config.test_units, oid_counter, true); + break; case 'w': /* warning threshold */ - { - mp_range_parsed tmp = mp_parse_range_string(optarg); - if (tmp.error != MP_PARSING_SUCCES) { - die(STATE_UNKNOWN, "Unable to parse warning threshold range: %s", optarg); - } - config.thresholds.warning = tmp.range; - config.thresholds.warning_is_set = true; - } break; + check_snmp_set_thresholds(optarg, config.test_units, oid_counter, false); + break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { /* diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c new file mode 100644 index 00000000..9db1d9f4 --- /dev/null +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -0,0 +1,103 @@ +#include "./check_snmp_helpers.h" +#include +#include "../../lib/utils_base.h" + +check_snmp_test_unit check_snmp_test_unit_init() { + check_snmp_test_unit tmp = { + .threshold = mp_thresholds_init(), + }; + return tmp; +} + + +int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], + size_t max_test_units, bool is_critical) { + if (strchr(threshold_string, ',') != NULL) { + // Got a comma in the string, should be multiple values + size_t tmp_counter = 0; + mp_range range_buffer; + bool first_value = true; + for (char *ptr = strtok(threshold_string, ", "); ptr != NULL; + ptr = strtok(NULL, ", "), tmp_counter++) { + + // edge case: maybe we got `,,` to skip a value + if (strlen(ptr) == 0) { + // use the previous value in this case + // or do not overwrite the loop value to be specific + if (first_value) { + die(STATE_UNKNOWN, "Empty threshold value"); + } + } else { + mp_range_parsed tmp = mp_parse_range_string(ptr); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr); + } + range_buffer = tmp.range; + } + + if (is_critical) { + tu[tmp_counter].threshold.critical = range_buffer; + tu[tmp_counter].threshold.critical_is_set = true; + } else { + tu[tmp_counter].threshold.warning = range_buffer; + tu[tmp_counter].threshold.warning_is_set = true; + } + first_value = false; + } + } else { + // Single value + mp_range_parsed tmp = mp_parse_range_string(threshold_string); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string); + } + + for (size_t i = 0; i < max_test_units; i++) { + if (is_critical) { + tu[i].threshold.critical = tmp.range; + tu[i].threshold.critical_is_set = true; + } else { + tu[i].threshold.warning = tmp.range; + tu[i].threshold.warning_is_set = true; + } + } + } + + return 0; +} + +const int DEFAULT_PROTOCOL = SNMP_VERSION_1; +const char DEFAULT_OUTPUT_DELIMITER[] = " "; + +const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; + +check_snmp_config check_snmp_config_init() { + check_snmp_config tmp = { + .use_getnext = false, + + .ignore_mib_parsing_errors = false, + .need_mibs = false, + + .test_units = NULL, + .num_of_test_units = 0, + + .nulloid_result = STATE_UNKNOWN, // state to return if no result for query + + .invert_search = true, + .regex_cmp_value = {}, + .string_cmp_value = "", + + .multiplier = 1.0, + .offset = 0, + + .use_perf_data_labels_from_input = false, + }; + + snmp_sess_init(&tmp.snmp_session); + + tmp.snmp_session.retries = DEFAULT_RETRIES; + tmp.snmp_session.version = DEFAULT_SNMP_VERSION; + tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + tmp.snmp_session.community = (unsigned char *)"public"; + tmp.snmp_session.community_len = strlen("public"); + return tmp; +} diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h new file mode 100644 index 00000000..74e7d0cd --- /dev/null +++ b/plugins/check_snmp.d/check_snmp_helpers.h @@ -0,0 +1,8 @@ +#pragma once + +#include "./config.h" + +check_snmp_test_unit check_snmp_test_unit_init(); +int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], + size_t max_test_units, bool is_critical); +check_snmp_config check_snmp_config_init(); diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index e2e1d6c2..ca624a81 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -1,11 +1,11 @@ #pragma once -#include "states.h" #include "thresholds.h" -#include "utils_base.h" +#include "states.h" #include #include #include +#include "../common.h" // defines for snmp libs #define u_char unsigned char @@ -18,12 +18,8 @@ #include #include -const int DEFAULT_PROTOCOL = SNMP_VERSION_1; -const char DEFAULT_PORT[] = "161"; -const char DEFAULT_OUTPUT_DELIMITER[] = " "; -const int DEFAULT_RETRIES = 5; - -const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; +#define DEFAULT_PORT "161" +#define DEFAULT_RETRIES 5 typedef struct eval_method { bool crit_string; @@ -35,13 +31,9 @@ typedef struct check_snmp_test_unit { char *label; char *unit_value; eval_method eval_mthd; + mp_thresholds threshold; } check_snmp_test_unit; -check_snmp_test_unit check_snmp_test_unit_init() { - check_snmp_test_unit tmp = {}; - return tmp; -} - typedef struct check_snmp_config { // SNMP session to use struct snmp_session snmp_session; @@ -55,7 +47,6 @@ typedef struct check_snmp_config { check_snmp_test_unit *test_units; size_t num_of_test_units; - mp_thresholds thresholds; // State if an empty value is encountered mp_state_enum nulloid_result; @@ -72,36 +63,3 @@ typedef struct check_snmp_config { // Modify output bool use_perf_data_labels_from_input; } check_snmp_config; - -check_snmp_config check_snmp_config_init() { - check_snmp_config tmp = { - .use_getnext = false, - - .ignore_mib_parsing_errors = false, - .need_mibs = false, - - .test_units = NULL, - .num_of_test_units = 0, - .thresholds = mp_thresholds_init(), - - .nulloid_result = STATE_UNKNOWN, // state to return if no result for query - - .invert_search = true, - .regex_cmp_value = {}, - .string_cmp_value = "", - - .multiplier = 1.0, - .offset = 0, - - .use_perf_data_labels_from_input = false, - }; - - snmp_sess_init(&tmp.snmp_session); - - tmp.snmp_session.retries = DEFAULT_RETRIES; - tmp.snmp_session.version = DEFAULT_SNMP_VERSION; - tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; - tmp.snmp_session.community = (unsigned char *)"public"; - tmp.snmp_session.community_len = strlen("public"); - return tmp; -} -- cgit v1.2.3-74-g34f1 From f976155863357e15961b280413244cc0badd89cb Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:41:02 +0200 Subject: check_snmp: Improve error handling --- plugins/check_snmp.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index b71ee4fd..badf9260 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -161,18 +161,27 @@ int main(int argc, char **argv) { struct snmp_session *active_session = snmp_open(&config.snmp_session); if (active_session == NULL) { - snmp_sess_perror("Failed to open session", &config.snmp_session); - die(STATE_UNKNOWN, "Failed to open SNMP session\n"); + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error (&config.snmp_session, &pcliberr , &psnmperr, &pperrstring); + die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); } struct snmp_pdu *response = NULL; int snmp_query_status = snmp_synch_response(active_session, pdu, &response); if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { - snmp_sess_perror("Failed to query", active_session); - // FAILED somehow - // TODO do some error analysis here - die(STATE_UNKNOWN, "SNMP query failed\n"); + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error (active_session, &pcliberr , &psnmperr, &pperrstring); + + if (psnmperr == SNMPERR_TIMEOUT) { + // We exit with critical here for some historical reason + die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); + } + die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); } snmp_close(active_session); -- cgit v1.2.3-74-g34f1 From e490c5f969b3844c1c48fcff129091fdca849d58 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:41:46 +0200 Subject: check_snmp: hopefully fix helpers --- plugins/check_snmp.d/check_snmp_helpers.c | 70 ++++++++++++++++++------------- plugins/check_snmp.d/check_snmp_helpers.h | 3 +- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 9db1d9f4..7725cc18 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -9,56 +9,66 @@ check_snmp_test_unit check_snmp_test_unit_init() { return tmp; } +int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit test_units[], + size_t max_test_units, bool is_critical) { + + if (threshold_string == NULL || strlen(threshold_string) == 0) { + // No input, do nothing + return 0; + } -int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], - size_t max_test_units, bool is_critical) { if (strchr(threshold_string, ',') != NULL) { // Got a comma in the string, should be multiple values - size_t tmp_counter = 0; - mp_range range_buffer; - bool first_value = true; + size_t tu_index = 0; + + while (threshold_string[0] == ',') { + // got commas at the beginning, so skip some values + tu_index++; + threshold_string++; + } + for (char *ptr = strtok(threshold_string, ", "); ptr != NULL; - ptr = strtok(NULL, ", "), tmp_counter++) { + ptr = strtok(NULL, ", "), tu_index++) { + + if (tu_index > max_test_units) { + // More thresholds then values, just ignore them + return 0; + } // edge case: maybe we got `,,` to skip a value if (strlen(ptr) == 0) { - // use the previous value in this case - // or do not overwrite the loop value to be specific - if (first_value) { - die(STATE_UNKNOWN, "Empty threshold value"); - } - } else { - mp_range_parsed tmp = mp_parse_range_string(ptr); - if (tmp.error != MP_PARSING_SUCCES) { - die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr); - } - range_buffer = tmp.range; + // no threshold given, do not set it then + continue; + } + + mp_range_parsed tmp = mp_parse_range_string(ptr); + if (tmp.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", ptr); } if (is_critical) { - tu[tmp_counter].threshold.critical = range_buffer; - tu[tmp_counter].threshold.critical_is_set = true; + test_units[tu_index].threshold.critical = tmp.range; + test_units[tu_index].threshold.critical_is_set = true; } else { - tu[tmp_counter].threshold.warning = range_buffer; - tu[tmp_counter].threshold.warning_is_set = true; + test_units[tu_index].threshold.warning = tmp.range; + test_units[tu_index].threshold.warning_is_set = true; } - first_value = false; } + } else { // Single value + // only valid for the first test unit mp_range_parsed tmp = mp_parse_range_string(threshold_string); if (tmp.error != MP_PARSING_SUCCES) { die(STATE_UNKNOWN, "Unable to parse critical threshold range: %s", threshold_string); } - for (size_t i = 0; i < max_test_units; i++) { - if (is_critical) { - tu[i].threshold.critical = tmp.range; - tu[i].threshold.critical_is_set = true; - } else { - tu[i].threshold.warning = tmp.range; - tu[i].threshold.warning_is_set = true; - } + if (is_critical) { + test_units[0].threshold.critical = tmp.range; + test_units[0].threshold.critical_is_set = true; + } else { + test_units[0].threshold.warning = tmp.range; + test_units[0].threshold.warning_is_set = true; } } diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h index 74e7d0cd..28e3c4e3 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.h +++ b/plugins/check_snmp.d/check_snmp_helpers.h @@ -3,6 +3,5 @@ #include "./config.h" check_snmp_test_unit check_snmp_test_unit_init(); -int check_snmp_set_thresholds(char *threshold_string, check_snmp_test_unit tu[], - size_t max_test_units, bool is_critical); +int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool); check_snmp_config check_snmp_config_init(); -- cgit v1.2.3-74-g34f1 From 015e4c098693e6234de0994879a173dd2463fefd Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:42:06 +0200 Subject: check_snmp: fix/adapt tests --- plugins/t/check_snmp.t | 103 ++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 62 deletions(-) diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t index 576cc506..99e01add 100644 --- a/plugins/t/check_snmp.t +++ b/plugins/t/check_snmp.t @@ -10,7 +10,7 @@ use NPTest; BEGIN { plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp"; - plan tests => 63; + plan tests => 42; } my $res; @@ -24,7 +24,7 @@ my $user_snmp = getTestParameter("NP_SNMP_USER", "An SNMP user", "auth_ $res = NPTest->testCmd( "./check_snmp -t 1" ); is( $res->return_code, 3, "No host name" ); -is( $res->output, "No host specified" ); +is( $res->output, "No OIDs specified" ); $res = NPTest->testCmd( "./check_snmp -H fakehostname --ignore-mib-parsing-errors" ); is( $res->return_code, 3, "No OIDs specified" ); @@ -32,145 +32,124 @@ is( $res->output, "No OIDs specified" ); $res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3 -U not_a_user --seclevel=rubbish" ); is( $res->return_code, 3, "Invalid seclevel" ); -like( $res->output, "/check_snmp: Invalid seclevel - rubbish/" ); +like( $res->output, "/invalid security level: rubbish/" ); $res = NPTest->testCmd( "./check_snmp -H fakehost --ignore-mib-parsing-errors -o oids -P 3c" ); is( $res->return_code, 3, "Invalid protocol" ); -like( $res->output, "/check_snmp: Invalid SNMP version - 3c/" ); +like( $res->output, "/invalid SNMP version/protocol: 3c/" ); SKIP: { skip "no snmp host defined", 50 if ( ! $host_snmp ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -P 2c -C $snmp_community -o system.sysUpTime.0 -w 1: -c 1:"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying uptime" ); - like($res->output, '/^SNMP OK - (\d+)/', "String contains SNMP OK"); - $res->output =~ /^SNMP OK - (\d+)/; + $res->output =~ /\|.*=(\d+);/; my $value = $1; cmp_ok( $value, ">", 0, "Got a time value" ); like($res->perf_output, "/sysUpTime.*$1/", "Got perfdata with value '$1' in it"); # some more threshold tests - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 0, "Threshold test -c 1:" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c ~:1 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Threshold test -c ~:1" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1:10 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Threshold test -c 1:10" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c \@1:10 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Threshold test -c \@1:10" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 10:1"); - cmp_ok( $res->return_code, '==', 0, "Threshold test -c 10:1" ); - - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o .1.3.6.1.2.1.1.3.0 -w 1: -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 0, "Test with numeric OID (no mibs loaded)" ); - like($res->output, '/^SNMP OK - \d+/', "String contains SNMP OK"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying sysDescr" ); unlike($res->perf_output, '/sysDescr/', "Perfdata doesn't contain string values"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0,system.sysDescr.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, comma-separated" ); - like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysDescr.0 -o system.sysDescr.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying two string OIDs, repeated option" ); - like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 1:1 -c 1:1 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrSWRunIndex.1" ); - like($res->output, '/^SNMP OK - 1\s.*$/', "String fits SNMP OK and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w 0 -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 1, "Exit WARNING when querying hrSWRunIndex.1 and warn-th doesn't apply " ); - like($res->output, '/^SNMP WARNING - \*1\*\s.*$/', "String matches SNMP WARNING and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w :0 -c 0 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL when querying hrSWRunIndex.1 and crit-th doesn't apply" ); - like($res->output, '/^SNMP CRITICAL - \*1\*\s.*$/', "String matches SNMP CRITICAL and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2 -c 1:2 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Checking two OIDs at once" ); - like($res->output, "/^SNMP OK - 2 1/", "Got two values back" ); - like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); - like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" ); + like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" ); + like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o ifIndex.2,ifIndex.1 -w 1:2,1:2 -c 2:2,2:2 -P 2c"); cmp_ok( $res->return_code, '==', 2, "Checking critical threshold is passed if any one value crosses" ); - like($res->output, "/^SNMP CRITICAL - 2 *1*/", "Got two values back" ); - like( $res->perf_output, "/ifIndex.2=2/", "Got 1st perf data" ); - like( $res->perf_output, "/ifIndex.1=1/", "Got 2nd perf data" ); + like( $res->perf_output, "/ifIndex.2'?=2/", "Got 1st perf data" ); + like( $res->perf_output, "/ifIndex.1'?=1/", "Got 2nd perf data" ); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w 1:,1: -c 1:,1: -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying hrMemorySize and hrSystemProcesses"); - like($res->output, '/^SNMP OK - \d+ \d+/', "String contains hrMemorySize and hrSystemProcesses"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w \@:0 -c \@0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Exit OK with inside-range thresholds"); - like($res->output, '/^SNMP OK - 1\s.*$/', "String matches SNMP OK and output format"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3"); - $res->output =~ m/^SNMP OK - (\d+\.\d{2})\s.*$/; + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -P 2c"); + $res->output =~ m/^.*Value: (\d+).*$/; my $lower = $1 - 0.05; my $higher = $1 + 0.05; - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoad.3 -w $lower -c $higher"); - cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractionnal arguments"); + # $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o enterprises.ucdavis.laTable.laEntry.laLoadInt.3 -w $lower -c $higher -P 2c"); + # cmp_ok( $res->return_code, '==', 1, "Exit WARNING with fractional arguments"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0,host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w ,:0 -c ,:2 -P 2c"); cmp_ok( $res->return_code, '==', 1, "Exit WARNING on 2nd threshold"); - like($res->output, '/^SNMP WARNING - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s+\*1\*\s.*$/', "First OID returned as string, 2nd checked for thresholds"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c ''"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrSWRun.hrSWRunTable.hrSWRunEntry.hrSWRunIndex.1 -w '' -c '' -P 2c"); cmp_ok( $res->return_code, '==', 0, "Empty thresholds doesn't crash"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,,1 -c ,,2 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Skipping first two thresholds on 2 OID check"); - like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping first two thresholds, result printed rather than parsed"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,,"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o host.hrStorage.hrMemorySize.0,host.hrSystem.hrSystemProcesses.0 -w ,, -c ,, -P 2c"); cmp_ok( $res->return_code, '==', 0, "Skipping all thresholds"); - like($res->output, '/^SNMP OK - \d+ \w+ \d+\s.*$/', "Skipping all thresholds, result printed rather than parsed"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec'"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -c 1000000000000: -u '1/100 sec' -P 2c"); cmp_ok( $res->return_code, '==', 2, "Timetick used as a threshold"); - like($res->output, '/^SNMP CRITICAL - \*\d+\* 1\/100 sec.*$/', "Timetick used as a threshold, parsed as numeric"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o system.sysUpTime.0 -P 2c"); cmp_ok( $res->return_code, '==', 0, "Timetick used as a string"); - like($res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "Timetick used as a string, result printed rather than parsed"); - $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1"); + $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -C $snmp_community -o HOST-RESOURCES-MIB::hrSWRunName.1 -P 2c"); cmp_ok( $res->return_code, '==', 0, "snmp response without datatype"); - like( $res->output, '/^SNMP OK - "(systemd|init)" \| $/', "snmp response without datatype" ); } SKIP: { skip "no SNMP user defined", 1 if ( ! $user_snmp ); $res = NPTest->testCmd( "./check_snmp -H $host_snmp --ignore-mib-parsing-errors -o HOST-RESOURCES-MIB::hrSystemUptime.0 -P 3 -U $user_snmp -L noAuthNoPriv"); - like( $res->output, '/^SNMP OK - Timeticks:\s\(\d+\)\s+(?:\d+ days?,\s+)?\d+:\d+:\d+\.\d+\s.*$/', "noAuthNoPriv security level works properly" ); } # These checks need a complete command line. An invalid community is used so # the tests can run on hosts w/o snmp host/community in NPTest.cache. Execution will fail anyway SKIP: { skip "no non responsive host defined", 2 if ( ! $host_nonresponsive ); - $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $host_nonresponsive --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 2, "Exit CRITICAL with non responsive host" ); - like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem"); + # like($res->output, '/Plugin timed out while executing system call/', "String matches timeout problem"); } SKIP: { skip "no non invalid host defined", 2 if ( ! $hostname_invalid ); - $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1:"); + $res = NPTest->testCmd( "./check_snmp -H $hostname_invalid --ignore-mib-parsing-errors -C np_foobar -o system.sysUpTime.0 -w 1: -c 1: -P 2c"); cmp_ok( $res->return_code, '==', 3, "Exit UNKNOWN with non responsive host" ); - like($res->output, '/External command error: .*(nosuchhost|Name or service not known|Unknown host).*/s', "String matches invalid host"); + like($res->output, '/.*Unknown host.*/s', "String matches invalid host"); } -- cgit v1.2.3-74-g34f1 From faf794b40139f02854b0737b2d62c5a039968762 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:43:11 +0200 Subject: check_snmp: remove leftover multiply function --- plugins/check_snmp.c | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index badf9260..3ac949a5 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -88,7 +88,6 @@ char *trim_whitespaces_and_check_quoting(char *str); char *get_next_argument(char *str); void print_usage(void); void print_help(void); -char *multiply(char *str, double multiplier, char *fmt_str); static int verbose = 0; @@ -954,53 +953,6 @@ char *get_next_argument(char *str) { return NULL; } -/* multiply result (values 0 < n < 1 work as divider) */ -char *multiply(char *str, double multiplier, char *fmt_str) { - - if (multiplier == 1) { - return (str); - } - - if (verbose > 2) { - printf(" multiply input: %s\n", str); - } - - char *endptr; - double val = strtod(str, &endptr); - if ((val == 0.0) && (endptr == str)) { - die(STATE_UNKNOWN, _("multiplier set (%.1f), but input is not a number: %s"), multiplier, - str); - } - - if (verbose > 2) { - printf(" multiply extracted double: %f\n", val); - } - - val *= multiplier; - char *conv = "%f"; - if (fmt_str != NULL) { - conv = fmt_str; - } - - char *buffer = calloc(1, DEFAULT_BUFFER_SIZE); - if (buffer == NULL) { - die(STATE_UNKNOWN, "calloc failed"); - } - - if (val == (int)val) { - snprintf(buffer, DEFAULT_BUFFER_SIZE, "%.0f", val); - } else { - if (verbose > 2) { - printf(" multiply using format: %s\n", conv); - } - snprintf(buffer, DEFAULT_BUFFER_SIZE, conv, val); - } - if (verbose > 2) { - printf(" multiply result: %s\n", buffer); - } - return buffer; -} - void print_help(void) { print_revision(progname, NP_VERSION); -- cgit v1.2.3-74-g34f1 From 75f792bc6bdb24371197774787aa312d94fd6773 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:43:32 +0200 Subject: check_snmp: declare internal functions static --- plugins/check_snmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 3ac949a5..c65e3d82 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -84,8 +84,8 @@ typedef struct proces_arguments_wrapper { } process_arguments_wrapper; static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -char *trim_whitespaces_and_check_quoting(char *str); -char *get_next_argument(char *str); +static char *trim_whitespaces_and_check_quoting(char *str); +static char *get_next_argument(char *str); void print_usage(void); void print_help(void); -- cgit v1.2.3-74-g34f1 From 78cb7b22886556b22ea39073abcba77b0f57a957 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:12:56 +0200 Subject: check_snmp: fix typos --- plugins/check_snmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index c65e3d82..bdf4b4f6 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -105,7 +105,7 @@ int main(int argc, char **argv) { np_set_args(argc, argv); - // Initialize net-snmp before touching the sessio we are going to use + // Initialize net-snmp before touching the session we are going to use init_snmp("check_snmp"); time_t current_time; @@ -190,10 +190,10 @@ int main(int argc, char **argv) { mp_check overall = mp_check_init(); - mp_subcheck sc_succesfull_query = mp_subcheck_init(); - xasprintf(&sc_succesfull_query.output, "SNMP query was succesful"); - sc_succesfull_query = mp_set_subcheck_state(sc_succesfull_query, STATE_OK); - mp_add_subcheck_to_check(&overall, sc_succesfull_query); + mp_subcheck sc_successfull_query = mp_subcheck_init(); + xasprintf(&sc_successfull_query.output, "SNMP query was succesful"); + sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_successfull_query); // We got the the query results, now process them size_t loop_index = 0; @@ -643,7 +643,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { // = usmAES256Cisco2PrivProtocol; config.snmp_session.securityAuthProtoLen = // sizeof(usmAES256Cisco2PrivProtocol) / sizeof(oid); } else { - die(STATE_UNKNOWN, "Unknow privacy protocol"); + die(STATE_UNKNOWN, "Unknown privacy protocol"); } break; case 'A': /* auth passwd */ -- cgit v1.2.3-74-g34f1 From 7ff0e518e4f77a8aae385511a7d46ff32e06bb9c Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:14:51 +0200 Subject: Add libsnmp-dev to github action dependencies --- .github/prepare_debian.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/prepare_debian.sh b/.github/prepare_debian.sh index f7b6cf9f..b5cacd4a 100755 --- a/.github/prepare_debian.sh +++ b/.github/prepare_debian.sh @@ -24,6 +24,7 @@ apt-get -y install perl \ libpq-dev \ libradcli-dev \ libnet-snmp-perl \ + libsnmp-dev \ procps \ libdbi0-dev \ libdbd-sqlite3 \ -- cgit v1.2.3-74-g34f1 From 776c51a66f6eb9c3d6f018cb3786c6441f6b7171 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:18:15 +0200 Subject: Add netsnmp lib to specfile --- .github/monitoring-plugins.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec index 10799128..e3352988 100644 --- a/.github/monitoring-plugins.spec +++ b/.github/monitoring-plugins.spec @@ -824,6 +824,7 @@ Provides check_smtp of the Monitoring Plugins. Summary: Monitoring Plugins - check_snmp Requires: %{name} = %{version}-%{release} Requires: net-snmp +Build-Requires: net-snmp-devel %description snmp Provides check_snmp of the Monitoring Plugins. -- cgit v1.2.3-74-g34f1 From f1104f49a43285bf406d5c853b1b85b2dc62f4c0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:20:41 +0200 Subject: Fix one more typo --- plugins/check_snmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index bdf4b4f6..7851631c 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -191,7 +191,7 @@ int main(int argc, char **argv) { mp_check overall = mp_check_init(); mp_subcheck sc_successfull_query = mp_subcheck_init(); - xasprintf(&sc_successfull_query.output, "SNMP query was succesful"); + xasprintf(&sc_successfull_query.output, "SNMP query was successful"); sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); mp_add_subcheck_to_check(&overall, sc_successfull_query); -- cgit v1.2.3-74-g34f1 From 334c7e3e13342995bacaf670f49b1333654520bd Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:27:07 +0200 Subject: Add libsnmp-dev dependency to codeql gh action --- .github/workflows/codeql-analysis.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e748d2eb..6227f98f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -57,9 +57,20 @@ jobs: run: | sudo apt update sudo apt-get install -y --no-install-recommends m4 gettext automake autoconf make build-essential - sudo apt-get install -y --no-install-recommends perl autotools-dev libdbi-dev libldap2-dev libpq-dev \ - libmysqlclient-dev libradcli-dev libkrb5-dev libdbi0-dev \ - libdbd-sqlite3 libssl-dev libcurl4-openssl-dev liburiparser-dev + sudo apt-get install -y --no-install-recommends perl \ + autotools-dev \ + libdbi-dev \ + libldap2-dev \ + libpq-dev \ + libmysqlclient-dev \ + libradcli-dev \ + libkrb5-dev \ + libdbi0-dev \ + libdbd-sqlite3 \ + libssl-dev \ + libcurl4-openssl-dev \ + liburiparser-dev \ + libsnmp-dev - name: Configure build run: | -- cgit v1.2.3-74-g34f1 From d3e1c0314d94e9f97c822fc80659c7234308045f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:29:56 +0200 Subject: Fix Specfile requires --- .github/monitoring-plugins.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/monitoring-plugins.spec b/.github/monitoring-plugins.spec index e3352988..ce22606b 100644 --- a/.github/monitoring-plugins.spec +++ b/.github/monitoring-plugins.spec @@ -88,6 +88,9 @@ BuildRequires: postgresql-devel # check_radius BuildRequires: radcli-devel +# check_snmp +BuildRequires: net-snmp-devel + %description Common files for Monitoring Plugins @@ -824,7 +827,6 @@ Provides check_smtp of the Monitoring Plugins. Summary: Monitoring Plugins - check_snmp Requires: %{name} = %{version}-%{release} Requires: net-snmp -Build-Requires: net-snmp-devel %description snmp Provides check_snmp of the Monitoring Plugins. -- cgit v1.2.3-74-g34f1 From bd6cff7f9ce4e03ee1d8d624c9e3f4c923467a73 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 27 Aug 2025 19:54:09 +0200 Subject: check_snmp: use snmp v2c if community is given --- plugins/check_snmp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 7851631c..35e1f339 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -516,6 +516,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { char *port = NULL; char *miblist = NULL; char *connection_prefix = NULL; + bool snmp_version_set_explicitely = false; // TODO error checking while (true) { int option_char = getopt_long( @@ -567,6 +568,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } else { die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); } + snmp_version_set_explicitely = true; + break; case 'N': /* SNMPv3 context name */ config.snmp_session.contextName = optarg; @@ -858,6 +861,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { setenv("MIBS", miblist, 1); } + // Historical default is SNMP v2c + if (!snmp_version_set_explicitely && config.snmp_session.community != NULL) { + config.snmp_session.version = SNMP_VERSION_2c; + } + if ((config.snmp_session.version == SNMP_VERSION_1) || (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ /* -- cgit v1.2.3-74-g34f1 From b65f94fe0ea13dcbdc7ff5f3d1241eab2685cddc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:24:16 +0200 Subject: Remove testing for SNMPGETNEXT from autotools stuff --- configure.ac | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/configure.ac b/configure.ac index ce140218..5f00b478 100644 --- a/configure.ac +++ b/configure.ac @@ -1470,17 +1470,6 @@ AC_ARG_WITH(snmpgetnext_command, AS_IF([test -n "$PATH_TO_SNMPGET"], [ AC_DEFINE_UNQUOTED(PATH_TO_SNMPGET,"$PATH_TO_SNMPGET",[path to snmpget binary]) EXTRAS="$EXTRAS check_hpjd" - - dnl PATH_TO_SNMPGETNEXT is used unconditionally in check_snmp: - dnl - dnl https://github.com/nagios-plugins/nagios-plugins/issues/788 - dnl - AS_IF([test -n "$PATH_TO_SNMPGETNEXT"], [ - AC_DEFINE_UNQUOTED(PATH_TO_SNMPGETNEXT,"$PATH_TO_SNMPGETNEXT",[path to snmpgetnext binary]) - EXTRAS="$EXTRAS check_snmp\$(EXEEXT)" - ], [ - AC_MSG_WARN([Get snmpgetnext from https://net-snmp.sourceforge.io/ to build the check_snmp plugin]) - ]) ], [ AC_MSG_WARN([Get snmpget from https://net-snmp.sourceforge.io/ to build the check_hpjd and check_snmp plugins]) ]) -- cgit v1.2.3-74-g34f1 From f5ad4275ff9b9810d784b89a47863e41bc8c368e Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:52:03 +0200 Subject: check_snmp: Test for availability of DES privacy protocol --- plugins/check_snmp.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 35e1f339..3403eee6 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -68,9 +68,15 @@ const char *email = "devel@monitoring-plugins.org"; const char DEFAULT_COMMUNITY[] = "public"; const char DEFAULT_MIBLIST[] = "ALL"; #define DEFAULT_AUTH_PROTOCOL "MD5" -#define DEFAULT_PRIV_PROTOCOL "DES" -#define DEFAULT_DELIMITER "=" -#define DEFAULT_BUFFER_SIZE 100 + +#ifdef usmDESPrivProtocol +# define DEFAULT_PRIV_PROTOCOL "DES" +#else +# define DEFAULT_PRIV_PROTOCOL "AES" +#endif + +#define DEFAULT_DELIMITER "=" +#define DEFAULT_BUFFER_SIZE 100 /* Longopts only arguments */ #define L_INVERT_SEARCH CHAR_MAX + 3 @@ -617,8 +623,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { +#ifdef usmDESPrivProtocol config.snmp_session.securityAuthProto = usmDESPrivProtocol; config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); +#else + die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform"); +#endif } else if (strcasecmp("AES", optarg) == 0) { config.snmp_session.securityAuthProto = usmAESPrivProtocol; config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); @@ -987,8 +997,13 @@ void print_help(void) { printf(" %s\n", _("SNMPv3 securityLevel")); printf(" %s\n", "-a, --authproto=[MD5|SHA]"); printf(" %s\n", _("SNMPv3 auth proto")); +#ifdef usmDESPrivProtocol printf(" %s\n", "-x, --privproto=[DES|AES]"); printf(" %s\n", _("SNMPv3 priv proto (default DES)")); +#else + printf(" %s\n", "-x, --privproto=[AES]"); + printf(" %s\n", _("SNMPv3 priv proto (default AES)")); +#endif /* Authentication Tokens*/ printf(" %s\n", "-C, --community=STRING"); -- cgit v1.2.3-74-g34f1 From 553a230a287b2b0294f0a0bbff7767493fe98657 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:52:15 +0200 Subject: check_snmp: formatting --- plugins/check_snmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 3403eee6..d0f01721 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -169,7 +169,7 @@ int main(int argc, char **argv) { int pcliberr = 0; int psnmperr = 0; char *pperrstring = NULL; - snmp_error (&config.snmp_session, &pcliberr , &psnmperr, &pperrstring); + snmp_error(&config.snmp_session, &pcliberr, &psnmperr, &pperrstring); die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); } @@ -180,7 +180,7 @@ int main(int argc, char **argv) { int pcliberr = 0; int psnmperr = 0; char *pperrstring = NULL; - snmp_error (active_session, &pcliberr , &psnmperr, &pperrstring); + snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); if (psnmperr == SNMPERR_TIMEOUT) { // We exit with critical here for some historical reason -- cgit v1.2.3-74-g34f1 From ebc2415330df963358b1e49beab0279a70ff4c84 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:12:27 +0200 Subject: check_snmp: fix DES availability detection(?) --- configure.ac | 10 ++++++++++ plugins/check_snmp.c | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 5f00b478..705183a2 100644 --- a/configure.ac +++ b/configure.ac @@ -1482,6 +1482,16 @@ else AC_MSG_WARN([Tried $PERL - install Net::SNMP perl module if you want to use the perl snmp plugins]) fi +dnl Check whether DES encryption is available (might not on RHEL) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include ]], [[oid *foo = usmDESPrivProtocol;]] + )], + [AC_DEFINE(HAVE_USM_DES_PRIV_PROTOCOL,1,Define whether we have DES Privacy Protocol)], + [] +) + AC_PATH_PROG(PATH_TO_QUAKESTAT,quakestat) AC_PATH_PROG(PATH_TO_QSTAT,qstat) AC_ARG_WITH(qstat_command, diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index d0f01721..6c672793 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -69,7 +69,7 @@ const char DEFAULT_COMMUNITY[] = "public"; const char DEFAULT_MIBLIST[] = "ALL"; #define DEFAULT_AUTH_PROTOCOL "MD5" -#ifdef usmDESPrivProtocol +#ifdef HAVE_USM_DES_PRIV_PROTOCOL # define DEFAULT_PRIV_PROTOCOL "DES" #else # define DEFAULT_PRIV_PROTOCOL "AES" @@ -623,7 +623,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { -#ifdef usmDESPrivProtocol +#ifdef HAVE_USM_DES_PRIV_PROTOCOL config.snmp_session.securityAuthProto = usmDESPrivProtocol; config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); #else @@ -997,7 +997,7 @@ void print_help(void) { printf(" %s\n", _("SNMPv3 securityLevel")); printf(" %s\n", "-a, --authproto=[MD5|SHA]"); printf(" %s\n", _("SNMPv3 auth proto")); -#ifdef usmDESPrivProtocol +#ifdef HAVE_USM_DES_PRIV_PROTOCOL printf(" %s\n", "-x, --privproto=[DES|AES]"); printf(" %s\n", _("SNMPv3 priv proto (default DES)")); #else -- cgit v1.2.3-74-g34f1 From e4c59440f0383cda12f6d4a4b1d34c38dbe10089 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:22:04 +0200 Subject: Build check_snmp unconditionally --- plugins/Makefile.am | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 38668348..f2f1777f 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -30,12 +30,13 @@ libexec_PROGRAMS = check_apt check_cluster check_disk check_dummy check_http che check_mrtg check_mrtgtraf check_ntp check_ntp_peer check_ping \ check_real check_smtp check_ssh check_tcp check_time check_ntp_time \ check_ups check_users negate \ - urlize @EXTRAS@ + urlize @EXTRAS@ \ + check_snmp check_tcp_programs = check_ftp check_imap check_nntp check_pop \ check_udp check_clamd @check_tcp_ssl@ -EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ +EXTRA_PROGRAMS = check_mysql check_radius check_pgsql 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 \ -- cgit v1.2.3-74-g34f1 From b4d84ebfee2cc93935ffead1f09a83ab0005ea10 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:39:43 +0200 Subject: check_snmp: Remove options description for input delimiter --- plugins/check_snmp.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 6c672793..1f4acb5a 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -75,9 +75,6 @@ const char DEFAULT_MIBLIST[] = "ALL"; # define DEFAULT_PRIV_PROTOCOL "AES" #endif -#define DEFAULT_DELIMITER "=" -#define DEFAULT_BUFFER_SIZE 100 - /* Longopts only arguments */ #define L_INVERT_SEARCH CHAR_MAX + 3 #define L_OFFSET CHAR_MAX + 4 @@ -1027,9 +1024,6 @@ void print_help(void) { printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'")); printf(" %s\n", _("for symbolic OIDs.)")); - printf(" %s\n", "-d, --delimiter=STRING"); - printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), - DEFAULT_DELIMITER); printf(" %s\n", _("Any data on the right hand side of the delimiter is considered")); printf(" %s\n", _("to be the data that should be used in the evaluation.")); printf(" %s\n", "-z, --nulloid=#"); -- cgit v1.2.3-74-g34f1 From 1ca5a6040a01cb5fc4f1ceb61b133826b22e1f7b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:50:53 +0200 Subject: check_snmp: Make linter happy --- plugins/check_snmp.c | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 1f4acb5a..0c1e8ebe 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -36,7 +36,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "./runcmd.h" #include "./utils.h" #include "../lib/states.h" -#include "../lib/utils_cmd.h" + #include "../lib/thresholds.h" #include "../lib/utils_base.h" #include "../lib/output.h" @@ -159,7 +159,8 @@ int main(int argc, char **argv) { } const int timeout_safety_tolerance = 5; - alarm(timeout_interval * config.snmp_session.retries + timeout_safety_tolerance); + alarm((timeout_interval * (unsigned int)config.snmp_session.retries) + + timeout_safety_tolerance); struct snmp_session *active_session = snmp_open(&config.snmp_session); if (active_session == NULL) { @@ -254,7 +255,7 @@ int main(int argc, char **argv) { sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); } } else if (config.test_units[loop_index].eval_mthd.crit_regex) { - const int nmatch = config.regex_cmp_value.re_nsub + 1; + const size_t nmatch = config.regex_cmp_value.re_nsub + 1; regmatch_t pmatch[nmatch]; memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); @@ -287,8 +288,8 @@ int main(int argc, char **argv) { } struct counter64 tmp = *(vars->val.counter64); uint64_t counter = (tmp.high << 32) + tmp.low; - counter *= config.multiplier; - counter += config.offset; + counter *= (uint64_t)config.multiplier; + counter += (uint64_t)config.offset; pd_result_val = mp_create_pd_value(counter); } break; /* Numerical values */ @@ -299,10 +300,10 @@ int main(int argc, char **argv) { if (verbose) { printf("Debug: Got a Integer like\n"); } - unsigned long tmp = *(vars->val.integer); - tmp *= config.multiplier; + unsigned long tmp = (unsigned long)*(vars->val.integer); + tmp *= (unsigned long)config.multiplier; - tmp += config.offset; + tmp += (unsigned long)config.offset; pd_result_val = mp_create_pd_value(tmp); break; } @@ -310,17 +311,18 @@ int main(int argc, char **argv) { if (verbose) { printf("Debug: Got a Integer\n"); } - unsigned long tmp = *(vars->val.integer); - tmp *= config.multiplier; - tmp += config.offset; + long tmp = *(vars->val.integer); + tmp *= (long)config.multiplier; + tmp += (long)config.offset; + pd_result_val = mp_create_pd_value(tmp); } break; case ASN_FLOAT: { if (verbose) { printf("Debug: Got a float\n"); } - float tmp = *(vars->val.floatVal); + double tmp = *(vars->val.floatVal); tmp *= config.multiplier; tmp += config.offset; @@ -437,7 +439,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { {"ipv4", no_argument, 0, '4'}, {"ipv6", no_argument, 0, '6'}, {"multiplier", required_argument, 0, 'M'}, - {"ignore-mib-parsing-errors", no_argument, false, L_IGNORE_MIB_PARSING_ERRORS}, + {"ignore-mib-parsing-errors", no_argument, 0, L_IGNORE_MIB_PARSING_ERRORS}, {"connection-prefix", required_argument, 0, L_CONNECTION_PREFIX}, {0, 0, 0, 0}}; @@ -674,7 +676,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (!is_integer(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); } else { - timeout_interval = atoi(optarg); + timeout_interval = (unsigned int)atoi(optarg); } break; @@ -895,10 +897,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { "No authentication passphrase was given, but authorization was requested"); } // auth and priv - size_t priv_key_generated = generate_Ku( - config.snmp_session.securityPrivProto, config.snmp_session.securityPrivProtoLen, - authpasswd, strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, - &config.snmp_session.securityPrivKeyLen); + int priv_key_generated = + generate_Ku(config.snmp_session.securityPrivProto, + (unsigned int)config.snmp_session.securityPrivProtoLen, authpasswd, + strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, + &config.snmp_session.securityPrivKeyLen); + if (priv_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); } @@ -908,10 +912,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (privpasswd == NULL) { die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); } - size_t auth_key_generated = generate_Ku( - config.snmp_session.securityAuthProto, config.snmp_session.securityAuthProtoLen, - privpasswd, strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, - &config.snmp_session.securityAuthKeyLen); + int auth_key_generated = + generate_Ku(config.snmp_session.securityAuthProto, + (unsigned int)config.snmp_session.securityAuthProtoLen, privpasswd, + strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, + &config.snmp_session.securityAuthKeyLen); + if (auth_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); } -- cgit v1.2.3-74-g34f1 From 77a5db04d7ccc954096c2d86c21fa7b05948072d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:16:36 +0200 Subject: check_snmp: implement output format setting --- plugins/check_snmp.c | 58 ++++++++++++++++++++++++++++++++----------- plugins/check_snmp.d/config.h | 3 +++ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 0c1e8ebe..a939f078 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -75,12 +75,6 @@ const char DEFAULT_MIBLIST[] = "ALL"; # define DEFAULT_PRIV_PROTOCOL "AES" #endif -/* Longopts only arguments */ -#define L_INVERT_SEARCH CHAR_MAX + 3 -#define L_OFFSET CHAR_MAX + 4 -#define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5 -#define L_CONNECTION_PREFIX CHAR_MAX + 6 - typedef struct proces_arguments_wrapper { int errorcode; check_snmp_config config; @@ -121,6 +115,10 @@ int main(int argc, char **argv) { check_snmp_config config = paw_tmp.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + if (config.ignore_mib_parsing_errors) { char *opt_toggle_res = snmp_mib_toggle_options("e"); if (opt_toggle_res != NULL) { @@ -406,6 +404,15 @@ int main(int argc, char **argv) { /* process command-line arguments */ static process_arguments_wrapper process_arguments(int argc, char **argv) { + enum { + /* Longopts only arguments */ + invert_search_index = CHAR_MAX + 1, + offset_index, + ignore_mib_parsing_errors_index, + connection_prefix_index, + output_format_index, + }; + static struct option longopts[] = { STD_LONG_OPTS, {"community", required_argument, 0, 'C'}, @@ -433,14 +440,15 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { {"authpasswd", required_argument, 0, 'A'}, {"privpasswd", required_argument, 0, 'X'}, {"next", no_argument, 0, 'n'}, - {"offset", required_argument, 0, L_OFFSET}, - {"invert-search", no_argument, 0, L_INVERT_SEARCH}, + {"offset", required_argument, 0, offset_index}, + {"invert-search", no_argument, 0, invert_search_index}, {"perf-oids", no_argument, 0, 'O'}, {"ipv4", no_argument, 0, '4'}, {"ipv6", no_argument, 0, '6'}, {"multiplier", required_argument, 0, 'M'}, - {"ignore-mib-parsing-errors", no_argument, 0, L_IGNORE_MIB_PARSING_ERRORS}, - {"connection-prefix", required_argument, 0, L_CONNECTION_PREFIX}, + {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index}, + {"connection-prefix", required_argument, 0, connection_prefix_index}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; if (argc < 2) { @@ -780,10 +788,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } unitv_counter++; } break; - case L_OFFSET: + case offset_index: config.offset = strtod(optarg, NULL); + config.offset_set = true; break; - case L_INVERT_SEARCH: + case invert_search_index: config.invert_search = false; break; case 'O': @@ -796,16 +805,34 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { case '6': connection_prefix = "udp6"; break; - case L_CONNECTION_PREFIX: + case connection_prefix_index: connection_prefix = optarg; break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { config.multiplier = strtod(optarg, NULL); + config.multiplier_set = true; } break; - case L_IGNORE_MIB_PARSING_ERRORS: + case ignore_mib_parsing_errors_index: config.ignore_mib_parsing_errors = true; + break; + case 'f': // Deprecated format option for floating point values + break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + config.output_format_is_set = true; + config.output_format = parser.output_format; + break; + } + default: + die(STATE_UNKNOWN, "Unknown option"); } } @@ -1068,6 +1095,7 @@ void print_help(void) { printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.').")); printf(" %s\n", "-M, --multiplier=FLOAT"); printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1")); + printf(UT_OUTPUT_FORMAT); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: " @@ -1115,5 +1143,5 @@ void print_usage(void) { printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n"); printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n"); printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n"); - printf("[-M multiplier [-f format]]\n"); + printf("[-M multiplier]\n"); } diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index ca624a81..e96dff5c 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -62,4 +62,7 @@ typedef struct check_snmp_config { // Modify output bool use_perf_data_labels_from_input; + + mp_output_format output_format; + bool output_format_is_set; } check_snmp_config; -- cgit v1.2.3-74-g34f1 From 7f1877f760a5ecdd0356010dd14c7606d44abfb0 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:18:42 +0200 Subject: check_snmp: Fix number processing (offset + multiplier) --- plugins/check_snmp.c | 71 +++++++++++++++++++++++++------ plugins/check_snmp.d/check_snmp_helpers.c | 2 + plugins/check_snmp.d/config.h | 2 + 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index a939f078..0a9c6752 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -286,9 +286,21 @@ int main(int argc, char **argv) { } struct counter64 tmp = *(vars->val.counter64); uint64_t counter = (tmp.high << 32) + tmp.low; - counter *= (uint64_t)config.multiplier; - counter += (uint64_t)config.offset; - pd_result_val = mp_create_pd_value(counter); + + if (config.multiplier_set || config.offset_set) { + double processed = 0; + if (config.multiplier_set) { + processed = (double)counter * config.multiplier; + } + + if (config.offset_set) { + processed += config.offset; + } + pd_result_val = mp_create_pd_value(processed); + } else { + pd_result_val = mp_create_pd_value(counter); + } + } break; /* Numerical values */ case ASN_GAUGE: // same as ASN_UNSIGNED @@ -299,10 +311,20 @@ int main(int argc, char **argv) { printf("Debug: Got a Integer like\n"); } unsigned long tmp = (unsigned long)*(vars->val.integer); - tmp *= (unsigned long)config.multiplier; - tmp += (unsigned long)config.offset; - pd_result_val = mp_create_pd_value(tmp); + if (config.multiplier_set || config.offset_set) { + double processed = 0; + if (config.multiplier_set) { + processed = (double)tmp * config.multiplier; + } + + if (config.offset_set) { + processed += config.offset; + } + pd_result_val = mp_create_pd_value(processed); + } else { + pd_result_val = mp_create_pd_value(tmp); + } break; } case ASN_INTEGER: { @@ -311,19 +333,36 @@ int main(int argc, char **argv) { } long tmp = *(vars->val.integer); - tmp *= (long)config.multiplier; - tmp += (long)config.offset; - pd_result_val = mp_create_pd_value(tmp); + if (config.multiplier_set || config.offset_set) { + double processed = 0; + if (config.multiplier_set) { + processed = (double)tmp * config.multiplier; + } + + if (config.offset_set) { + processed += config.offset; + } + pd_result_val = mp_create_pd_value(processed); + } else { + pd_result_val = mp_create_pd_value(tmp); + } + } break; case ASN_FLOAT: { if (verbose) { printf("Debug: Got a float\n"); } double tmp = *(vars->val.floatVal); - tmp *= config.multiplier; - tmp += config.offset; + if (config.multiplier_set) { + tmp *= config.multiplier; + } + + if (config.offset_set) { + tmp += config.offset; + } + pd_result_val = mp_create_pd_value(tmp); break; } @@ -332,8 +371,14 @@ int main(int argc, char **argv) { printf("Debug: Got a double\n"); } double tmp = *(vars->val.doubleVal); - tmp *= config.multiplier; - tmp += config.offset; + if (config.multiplier_set) { + tmp *= config.multiplier; + } + + if (config.offset_set) { + tmp += config.offset; + } + pd_result_val = mp_create_pd_value(tmp); break; } diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 7725cc18..8f4bcb9c 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -97,7 +97,9 @@ check_snmp_config check_snmp_config_init() { .string_cmp_value = "", .multiplier = 1.0, + .multiplier_set = false, .offset = 0, + .offset_set = false, .use_perf_data_labels_from_input = false, }; diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index e96dff5c..e68986e2 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -58,7 +58,9 @@ typedef struct check_snmp_config { // Modify data double multiplier; + bool multiplier_set; double offset; + bool offset_set; // Modify output bool use_perf_data_labels_from_input; -- cgit v1.2.3-74-g34f1 From a7c6760cfe46fce4010056b8cdc36a1c9bd1d366 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 30 Aug 2025 14:19:06 +0200 Subject: check_snmp: Small improvements + fix dereference bug --- plugins/check_snmp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 0a9c6752..d5abd8c2 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -214,8 +214,7 @@ int main(int argc, char **argv) { sc_oid_test.output = strdup(""); } - char oid_string[(MAX_OID_LEN * 2) + 1]; - memset(oid_string, 0, (MAX_OID_LEN * 2) + 1); + char oid_string[(MAX_OID_LEN * 2) + 1] = {}; int oid_string_result = snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length); @@ -404,9 +403,11 @@ int main(int argc, char **argv) { // Use oid for perdata label pd_num_val.label = strdup(oid_string); // TODO strdup error checking - } else if (config.test_units[loop_index].label != NULL || + } else if (config.test_units[loop_index].label != NULL && strcmp(config.test_units[loop_index].label, "") != 0) { pd_num_val.label = config.test_units[loop_index].label; + } else { + pd_num_val.label = config.test_units[loop_index].oid; } if (config.test_units[loop_index].unit_value != NULL && -- cgit v1.2.3-74-g34f1 From 41d309d438800102fc8aed736642e22da9f599fc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:22:58 +0200 Subject: check_snmp: improve string quoting in result --- plugins/check_snmp.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index d5abd8c2..3c054259 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -235,8 +235,23 @@ int main(int argc, char **argv) { if (verbose) { printf("Debug: Got a string\n"); } + char *tmp = (char *)vars->val.string; - xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + + if (strchr(tmp, '"') != NULL) { + // got double quote in the string + if (strchr(tmp, '\'') != NULL) { + // got single quote in the string too + // dont quote that at all to avoid even more confusion + xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + } else { + // quote with single quotes + xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); + } + } else { + // quote with double quotes + xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); + } if (strlen(tmp) == 0) { sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result); -- cgit v1.2.3-74-g34f1 From 888cd29202df5b99d4cd7834b11b030f2c39859f Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:24:44 +0200 Subject: lib/utils_base.c: clang-format --- lib/utils_base.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/utils_base.c b/lib/utils_base.c index 1e92b29b..5ba865c9 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -33,12 +33,12 @@ #include #include -#define np_free(ptr) \ - { \ - if (ptr) { \ - free(ptr); \ - ptr = NULL; \ - } \ +#define np_free(ptr) \ + { \ + if (ptr) { \ + free(ptr); \ + ptr = NULL; \ + } \ } monitoring_plugin *this_monitoring_plugin = NULL; @@ -153,7 +153,8 @@ range *parse_range_string(char *str) { set_range_end(temp_range, end); } - if (temp_range->start_infinity == true || temp_range->end_infinity == true || temp_range->start <= temp_range->end) { + if (temp_range->start_infinity == true || temp_range->end_infinity == true || + temp_range->start <= temp_range->end) { return temp_range; } free(temp_range); @@ -205,12 +206,14 @@ void print_thresholds(const char *threshold_name, thresholds *my_threshold) { printf("Threshold not set"); } else { if (my_threshold->warning) { - printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end); + printf("Warning: start=%g end=%g; ", my_threshold->warning->start, + my_threshold->warning->end); } else { printf("Warning not set; "); } if (my_threshold->critical) { - printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end); + printf("Critical: start=%g end=%g", my_threshold->critical->start, + my_threshold->critical->end); } else { printf("Critical not set"); } @@ -225,7 +228,8 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { if (!my_range.end_infinity && !my_range.start_infinity) { // range: .........|---inside---|........... // value - is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) && (cmp_perfdata_value(value, my_range.end) <= 0)); + is_inside = ((cmp_perfdata_value(value, my_range.start) >= 0) && + (cmp_perfdata_value(value, my_range.end) <= 0)); } else if (!my_range.start_infinity && my_range.end_infinity) { // range: .........|---inside--------- // value @@ -239,7 +243,8 @@ bool mp_check_range(const mp_perfdata_value value, const mp_range my_range) { is_inside = true; } - if ((is_inside && my_range.alert_on_inside_range == INSIDE) || (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) { + if ((is_inside && my_range.alert_on_inside_range == INSIDE) || + (!is_inside && my_range.alert_on_inside_range == OUTSIDE)) { return true; } @@ -557,8 +562,8 @@ void np_enable_state(char *keyname, int expected_data_version) { this_state->state_data = NULL; /* Calculate filename */ - ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), (unsigned long)geteuid(), - this_monitoring_plugin->plugin_name, this_state->name); + ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), + (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); if (ret < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } -- cgit v1.2.3-74-g34f1 From 28bb2fa0a499b46e279244990b8268c1ed8823bc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:27:49 +0200 Subject: lib/utils_base.c: small refactoring --- lib/perfdata.h | 2 +- lib/utils_base.c | 195 ++++++++++++++++++++++++++----------------------------- 2 files changed, 94 insertions(+), 103 deletions(-) diff --git a/lib/perfdata.h b/lib/perfdata.h index 7fd908a9..c5d4a61d 100644 --- a/lib/perfdata.h +++ b/lib/perfdata.h @@ -45,7 +45,7 @@ typedef struct range_struct { double start; bool start_infinity; double end; - int end_infinity; + bool end_infinity; int alert_on; /* OUTSIDE (default) or INSIDE */ char *text; /* original unparsed text input */ } range; diff --git a/lib/utils_base.c b/lib/utils_base.c index 5ba865c9..43e88e7a 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -46,7 +46,7 @@ monitoring_plugin *this_monitoring_plugin = NULL; int timeout_state = STATE_CRITICAL; unsigned int timeout_interval = DEFAULT_SOCKET_TIMEOUT; -bool _np_state_read_file(FILE *); +bool _np_state_read_file(FILE *state_file); void np_init(char *plugin_name, int argc, char **argv) { if (this_monitoring_plugin == NULL) { @@ -153,7 +153,7 @@ range *parse_range_string(char *str) { set_range_end(temp_range, end); } - if (temp_range->start_infinity == true || temp_range->end_infinity == true || + if (temp_range->start_infinity || temp_range->end_infinity || temp_range->start <= temp_range->end) { return temp_range; } @@ -261,21 +261,21 @@ bool check_range(double value, range *my_range) { yes = false; } - if (my_range->end_infinity == false && my_range->start_infinity == false) { + if (!my_range->end_infinity && !my_range->start_infinity) { if ((my_range->start <= value) && (value <= my_range->end)) { return no; } return yes; } - if (my_range->start_infinity == false && my_range->end_infinity == true) { + if (!my_range->start_infinity && my_range->end_infinity) { if (my_range->start <= value) { return no; } return yes; } - if (my_range->start_infinity == true && my_range->end_infinity == false) { + if (my_range->start_infinity && !my_range->end_infinity) { if (value <= my_range->end) { return no; } @@ -287,12 +287,12 @@ bool check_range(double value, range *my_range) { /* Returns status */ int get_status(double value, thresholds *my_thresholds) { if (my_thresholds->critical != NULL) { - if (check_range(value, my_thresholds->critical) == true) { + if (check_range(value, my_thresholds->critical)) { return STATE_CRITICAL; } } if (my_thresholds->warning != NULL) { - if (check_range(value, my_thresholds->warning) == true) { + if (check_range(value, my_thresholds->warning)) { return STATE_WARNING; } } @@ -301,32 +301,31 @@ int get_status(double value, thresholds *my_thresholds) { char *np_escaped_string(const char *string) { char *data; - int i; - int j = 0; + int write_index = 0; data = strdup(string); - for (i = 0; data[i]; i++) { + for (int i = 0; data[i]; i++) { if (data[i] == '\\') { switch (data[++i]) { case 'n': - data[j++] = '\n'; + data[write_index++] = '\n'; break; case 'r': - data[j++] = '\r'; + data[write_index++] = '\r'; break; case 't': - data[j++] = '\t'; + data[write_index++] = '\t'; break; case '\\': - data[j++] = '\\'; + data[write_index++] = '\\'; break; default: - data[j++] = data[i]; + data[write_index++] = data[i]; } } else { - data[j++] = data[i]; + data[write_index++] = data[i]; } } - data[j] = '\0'; + data[write_index] = '\0'; return data; } @@ -341,33 +340,35 @@ int np_check_if_root(void) { return (geteuid() == 0); } char *np_extract_value(const char *varlist, const char *name, char sep) { char *tmp = NULL; char *value = NULL; - int i; - while (1) { + while (true) { /* Strip any leading space */ - for (; isspace(varlist[0]); varlist++) + for (; isspace(varlist[0]); varlist++) { ; + } if (strncmp(name, varlist, strlen(name)) == 0) { varlist += strlen(name); /* strip trailing spaces */ - for (; isspace(varlist[0]); varlist++) + for (; isspace(varlist[0]); varlist++) { ; + } if (varlist[0] == '=') { /* We matched the key, go past the = sign */ varlist++; /* strip leading spaces */ - for (; isspace(varlist[0]); varlist++) + for (; isspace(varlist[0]); varlist++) { ; + } if ((tmp = index(varlist, sep))) { /* Value is delimited by a comma */ if (tmp - varlist == 0) { continue; } - value = (char *)calloc(1, tmp - varlist + 1); - strncpy(value, varlist, tmp - varlist); + value = (char *)calloc(1, (unsigned long)(tmp - varlist + 1)); + strncpy(value, varlist, (unsigned long)(tmp - varlist)); value[tmp - varlist] = '\0'; } else { /* Value is delimited by a \0 */ @@ -392,7 +393,7 @@ char *np_extract_value(const char *varlist, const char *name, char sep) { /* Clean-up trailing spaces/newlines */ if (value) { - for (i = strlen(value) - 1; isspace(value[i]); i--) { + for (unsigned long i = strlen(value) - 1; isspace(value[i]); i--) { value[i] = '\0'; } } @@ -441,11 +442,7 @@ int mp_translate_state(char *state_text) { * parse of argv, so that uniqueness in parameters are reflected there. */ char *_np_state_generate_key(void) { - int i; char **argv = this_monitoring_plugin->argv; - char keyname[41]; - char *p = NULL; - unsigned char result[256]; #ifdef USE_OPENSSL @@ -458,7 +455,7 @@ char *_np_state_generate_key(void) { EVP_DigestInit(ctx, EVP_sha256()); - for (i = 0; i < this_monitoring_plugin->argc; i++) { + for (int i = 0; i < this_monitoring_plugin->argc; i++) { EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); } @@ -467,24 +464,26 @@ char *_np_state_generate_key(void) { struct sha256_ctx ctx; - for (i = 0; i < this_monitoring_plugin->argc; i++) { + for (int i = 0; i < this_monitoring_plugin->argc; i++) { sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); } sha256_finish_ctx(&ctx, result); #endif // FOUNDOPENSSL - for (i = 0; i < 20; ++i) { + char keyname[41]; + for (int i = 0; i < 20; ++i) { sprintf(&keyname[2 * i], "%02x", result[i]); } keyname[40] = '\0'; - p = strdup(keyname); - if (p == NULL) { + char *keyname_copy = strdup(keyname); + if (keyname_copy == NULL) { die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); } - return p; + + return keyname_copy; } void _cleanup_state_data(void) { @@ -525,21 +524,16 @@ char *_np_state_calculate_location_prefix(void) { * UNKNOWN if exception */ void np_enable_state(char *keyname, int expected_data_version) { - state_key *this_state = NULL; - char *temp_filename = NULL; - char *temp_keyname = NULL; - char *p = NULL; - int ret; - if (this_monitoring_plugin == NULL) { die(STATE_UNKNOWN, _("This requires np_init to be called")); } - this_state = (state_key *)calloc(1, sizeof(state_key)); + state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); if (this_state == NULL) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } + char *temp_keyname = NULL; if (keyname == NULL) { temp_keyname = _np_state_generate_key(); } else { @@ -548,13 +542,14 @@ void np_enable_state(char *keyname, int expected_data_version) { die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); } } + /* Die if invalid characters used for keyname */ - p = temp_keyname; - while (*p != '\0') { - if (!(isalnum(*p) || *p == '_')) { + char *tmp_char = temp_keyname; + while (*tmp_char != '\0') { + if (!(isalnum(*tmp_char) || *tmp_char == '_')) { die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); } - p++; + tmp_char++; } this_state->name = temp_keyname; this_state->plugin_name = this_monitoring_plugin->plugin_name; @@ -562,9 +557,11 @@ void np_enable_state(char *keyname, int expected_data_version) { this_state->state_data = NULL; /* Calculate filename */ - ret = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), - (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); - if (ret < 0) { + char *temp_filename = NULL; + int error = + asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), + (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); + if (error < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } @@ -581,19 +578,17 @@ void np_enable_state(char *keyname, int expected_data_version) { * if exceptional error. */ state_data *np_state_read(void) { - state_data *this_state_data = NULL; - FILE *statefile; - bool rc = false; - if (this_monitoring_plugin == NULL) { die(STATE_UNKNOWN, _("This requires np_init to be called")); } + bool error_code = false; + /* Open file. If this fails, no previous state found */ - statefile = fopen(this_monitoring_plugin->state->_filename, "r"); + FILE *statefile = fopen(this_monitoring_plugin->state->_filename, "r"); if (statefile != NULL) { - this_state_data = (state_data *)calloc(1, sizeof(state_data)); + state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); if (this_state_data == NULL) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } @@ -601,12 +596,12 @@ state_data *np_state_read(void) { this_state_data->data = NULL; this_monitoring_plugin->state->state_data = this_state_data; - rc = _np_state_read_file(statefile); + error_code = _np_state_read_file(statefile); fclose(statefile); } - if (!rc) { + if (!error_code) { _cleanup_state_data(); } @@ -616,13 +611,17 @@ state_data *np_state_read(void) { /* * Read the state file */ -bool _np_state_read_file(FILE *f) { +bool _np_state_read_file(FILE *state_file) { + time_t current_time; + time(¤t_time); + + /* Note: This introduces a limit of 1024 bytes in the string data */ + char *line = (char *)calloc(1, 1024); + if (line == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + bool status = false; - size_t pos; - char *line; - int i; - int failure = 0; - time_t current_time, data_time; enum { STATE_FILE_VERSION, STATE_DATA_VERSION, @@ -631,16 +630,9 @@ bool _np_state_read_file(FILE *f) { STATE_DATA_END } expected = STATE_FILE_VERSION; - time(¤t_time); - - /* Note: This introduces a limit of 1024 bytes in the string data */ - line = (char *)calloc(1, 1024); - if (line == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - while (!failure && (fgets(line, 1024, f)) != NULL) { - pos = strlen(line); + int failure = 0; + while (!failure && (fgets(line, 1024, state_file)) != NULL) { + size_t pos = strlen(line); if (line[pos - 1] == '\n') { line[pos - 1] = '\0'; } @@ -650,32 +642,32 @@ bool _np_state_read_file(FILE *f) { } switch (expected) { - case STATE_FILE_VERSION: - i = atoi(line); + case STATE_FILE_VERSION: { + int i = atoi(line); if (i != NP_STATE_FORMAT_VERSION) { failure++; } else { expected = STATE_DATA_VERSION; } - break; - case STATE_DATA_VERSION: - i = atoi(line); + } break; + case STATE_DATA_VERSION: { + int i = atoi(line); if (i != this_monitoring_plugin->state->data_version) { failure++; } else { expected = STATE_DATA_TIME; } - break; - case STATE_DATA_TIME: + } break; + case STATE_DATA_TIME: { /* If time > now, error */ - data_time = strtoul(line, NULL, 10); + time_t data_time = strtoul(line, NULL, 10); if (data_time > current_time) { failure++; } else { this_monitoring_plugin->state->state_data->time = data_time; expected = STATE_DATA_TEXT; } - break; + } break; case STATE_DATA_TEXT: this_monitoring_plugin->state->state_data->data = strdup(line); if (this_monitoring_plugin->state->state_data->data == NULL) { @@ -700,27 +692,24 @@ bool _np_state_read_file(FILE *f) { * Will die with UNKNOWN if errors */ void np_state_write_string(time_t data_time, char *data_string) { - FILE *fp; - char *temp_file = NULL; - int fd = 0, result = 0; time_t current_time; - char *directories = NULL; - char *p = NULL; - if (data_time == 0) { time(¤t_time); } else { current_time = data_time; } + int result = 0; + /* If file doesn't currently exist, create directories */ if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) { + char *directories = NULL; result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename); if (result < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } - for (p = directories + 1; *p; p++) { + for (char *p = directories + 1; *p; p++) { if (*p == '/') { *p = '\0'; if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { @@ -734,37 +723,39 @@ void np_state_write_string(time_t data_time, char *data_string) { np_free(directories); } + char *temp_file = NULL; result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename); if (result < 0) { die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); } - if ((fd = mkstemp(temp_file)) == -1) { + int temp_file_desc = 0; + if ((temp_file_desc = mkstemp(temp_file)) == -1) { np_free(temp_file); die(STATE_UNKNOWN, _("Cannot create temporary filename")); } - fp = (FILE *)fdopen(fd, "w"); - if (fp == NULL) { - close(fd); + FILE *temp_file_pointer = (FILE *)fdopen(temp_file_desc, "w"); + if (temp_file_pointer == NULL) { + close(temp_file_desc); unlink(temp_file); np_free(temp_file); die(STATE_UNKNOWN, _("Unable to open temporary state file")); } - fprintf(fp, "# NP State file\n"); - fprintf(fp, "%d\n", NP_STATE_FORMAT_VERSION); - fprintf(fp, "%d\n", this_monitoring_plugin->state->data_version); - fprintf(fp, "%lu\n", current_time); - fprintf(fp, "%s\n", data_string); + fprintf(temp_file_pointer, "# NP State file\n"); + fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); + fprintf(temp_file_pointer, "%d\n", this_monitoring_plugin->state->data_version); + fprintf(temp_file_pointer, "%lu\n", current_time); + fprintf(temp_file_pointer, "%s\n", data_string); - fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP); + fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); - fflush(fp); + fflush(temp_file_pointer); - result = fclose(fp); + result = fclose(temp_file_pointer); - fsync(fd); + fsync(temp_file_desc); if (result != 0) { unlink(temp_file); -- cgit v1.2.3-74-g34f1 From 1aefb1f9df5268ccbcd3ce38f5527ebca3896db6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:54:08 +0200 Subject: snmp: fix complaint of snmpd about paths --- plugins/tests/conf/snmpd.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tests/conf/snmpd.conf b/plugins/tests/conf/snmpd.conf index eff5b0b3..1724c027 100644 --- a/plugins/tests/conf/snmpd.conf +++ b/plugins/tests/conf/snmpd.conf @@ -19,5 +19,5 @@ syscontact Alice # Embedded Subagents ############################################################################### -perl do "tests/check_snmp_agent.pl"; +perl do "./tests/check_snmp_agent.pl"; -- cgit v1.2.3-74-g34f1 From 87195f5511bf18db2a64f71ea9783ebbfb33c3a5 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:57:06 +0200 Subject: check_snmp: refactoring + fixes This commit moves the state retention logic to check_snmp as it is only used there and I do not want it to be used at all, so it doesn't get a place in the lib. Otherwise this adapts tests and fixes the rate computing in the refactored version of check_snmp. Also fixes some bugs detected with the tests --- lib/Makefile.am | 2 +- lib/tests/test_utils.c | 190 +------ lib/utils_base.c | 344 ------------ lib/utils_base.h | 22 - plugins/Makefile.am | 14 +- plugins/check_snmp.c | 778 +++++++++++++-------------- plugins/check_snmp.d/check_snmp_helpers.c | 861 +++++++++++++++++++++++++++++- plugins/check_snmp.d/check_snmp_helpers.h | 64 +++ plugins/check_snmp.d/config.h | 25 +- plugins/tests/check_snmp.t | 159 +++--- plugins/tests/check_snmp_agent.pl | 78 ++- plugins/tests/test_check_snmp.c | 175 ++++++ plugins/tests/test_check_snmp.t | 6 + 13 files changed, 1647 insertions(+), 1071 deletions(-) create mode 100644 plugins/tests/test_check_snmp.c create mode 100755 plugins/tests/test_check_snmp.t diff --git a/lib/Makefile.am b/lib/Makefile.am index a9f3ff40..27a08278 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -4,7 +4,7 @@ SUBDIRS = . tests noinst_LIBRARIES = libmonitoringplug.a -AM_CPPFLAGS = -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ +AM_CPPFLAGS = \ -I$(srcdir) -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/plugins libmonitoringplug_a_SOURCES = utils_base.c utils_tcp.c utils_cmd.c maxfd.c output.c perfdata.c output.c thresholds.c vendor/cJSON/cJSON.c diff --git a/lib/tests/test_utils.c b/lib/tests/test_utils.c index c3150f00..8040dec8 100644 --- a/lib/tests/test_utils.c +++ b/lib/tests/test_utils.c @@ -28,17 +28,7 @@ #include "utils_base.c" int main(int argc, char **argv) { - char state_path[1024]; - range *range; - double temp; - thresholds *thresholds = NULL; - int i, rc; - char *temp_string; - state_key *temp_state_key = NULL; - state_data *temp_state_data; - time_t current_time; - - plan_tests(185); + plan_tests(155); ok(this_monitoring_plugin == NULL, "monitoring_plugin not initialised"); @@ -57,7 +47,7 @@ int main(int argc, char **argv) { np_set_args(argc, argv); - range = parse_range_string("6"); + range *range = parse_range_string("6"); ok(range != NULL, "'6' is valid range"); ok(range->start == 0, "Start correct"); ok(range->start_infinity == false, "Not using negative infinity"); @@ -97,7 +87,7 @@ int main(int argc, char **argv) { free(range); range = parse_range_string("12345678901234567890:"); - temp = atof("12345678901234567890"); /* Can't just use this because number too large */ + double temp = atof("12345678901234567890"); /* Can't just use this because number too large */ ok(range != NULL, "'12345678901234567890:' is valid range"); ok(range->start == temp, "Start correct"); ok(range->start_infinity == false, "Not using negative infinity"); @@ -158,32 +148,34 @@ int main(int argc, char **argv) { range = parse_range_string("2:1"); ok(range == NULL, "'2:1' rejected"); - rc = _set_thresholds(&thresholds, NULL, NULL); - ok(rc == 0, "Thresholds (NULL, NULL) set"); + thresholds *thresholds = NULL; + int returnCode; + returnCode = _set_thresholds(&thresholds, NULL, NULL); + ok(returnCode == 0, "Thresholds (NULL, NULL) set"); ok(thresholds->warning == NULL, "Warning not set"); ok(thresholds->critical == NULL, "Critical not set"); - rc = _set_thresholds(&thresholds, NULL, "80"); - ok(rc == 0, "Thresholds (NULL, '80') set"); + returnCode = _set_thresholds(&thresholds, NULL, "80"); + ok(returnCode == 0, "Thresholds (NULL, '80') set"); ok(thresholds->warning == NULL, "Warning not set"); ok(thresholds->critical->end == 80, "Critical set correctly"); - rc = _set_thresholds(&thresholds, "5:33", NULL); - ok(rc == 0, "Thresholds ('5:33', NULL) set"); + returnCode = _set_thresholds(&thresholds, "5:33", NULL); + ok(returnCode == 0, "Thresholds ('5:33', NULL) set"); ok(thresholds->warning->start == 5, "Warning start set"); ok(thresholds->warning->end == 33, "Warning end set"); ok(thresholds->critical == NULL, "Critical not set"); - rc = _set_thresholds(&thresholds, "30", "60"); - ok(rc == 0, "Thresholds ('30', '60') set"); + returnCode = _set_thresholds(&thresholds, "30", "60"); + ok(returnCode == 0, "Thresholds ('30', '60') set"); ok(thresholds->warning->end == 30, "Warning set correctly"); ok(thresholds->critical->end == 60, "Critical set correctly"); ok(get_status(15.3, thresholds) == STATE_OK, "15.3 - ok"); ok(get_status(30.0001, thresholds) == STATE_WARNING, "30.0001 - warning"); ok(get_status(69, thresholds) == STATE_CRITICAL, "69 - critical"); - rc = _set_thresholds(&thresholds, "-10:-2", "-30:20"); - ok(rc == 0, "Thresholds ('-30:20', '-10:-2') set"); + returnCode = _set_thresholds(&thresholds, "-10:-2", "-30:20"); + ok(returnCode == 0, "Thresholds ('-30:20', '-10:-2') set"); ok(thresholds->warning->start == -10, "Warning start set correctly"); ok(thresholds->warning->end == -2, "Warning end set correctly"); ok(thresholds->critical->start == -30, "Critical start set correctly"); @@ -304,164 +296,28 @@ int main(int argc, char **argv) { test = np_extract_ntpvar("", "foo"); ok(!test, "Empty string return NULL"); - /* This is the result of running ./test_utils */ - temp_string = (char *)_np_state_generate_key(); - ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got hash with exe and no parameters") || - diag("You are probably running in wrong directory. Must run as ./test_utils"); - - this_monitoring_plugin->argc = 4; - this_monitoring_plugin->argv[0] = "./test_utils"; - this_monitoring_plugin->argv[1] = "here"; - this_monitoring_plugin->argv[2] = "--and"; - this_monitoring_plugin->argv[3] = "now"; - temp_string = (char *)_np_state_generate_key(); - ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), "Got based on expected argv"); - - unsetenv("MP_STATE_PATH"); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory"); - - setenv("MP_STATE_PATH", "", 1); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string"); - - setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1); - temp_string = (char *)_np_state_calculate_location_prefix(); - ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory"); - - ok(temp_state_key == NULL, "temp_state_key initially empty"); - - this_monitoring_plugin->argc = 1; - this_monitoring_plugin->argv[0] = "./test_utils"; - np_enable_state(NULL, 51); - temp_state_key = this_monitoring_plugin->state; - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), "Got generated filename"); - - np_enable_state("allowedchars_in_keyname", 77); - temp_state_key = this_monitoring_plugin->state; - sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", (unsigned long)geteuid()); - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "allowedchars_in_keyname"), "Got key name with valid chars"); - ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename"); - - /* Don't do this test just yet. Will die */ - /* - np_enable_state("bad^chars$in@here", 77); - temp_state_key = this_monitoring_plugin->state; - ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" ); - */ - - np_enable_state("funnykeyname", 54); - temp_state_key = this_monitoring_plugin->state; - sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", (unsigned long)geteuid()); - ok(!strcmp(temp_state_key->plugin_name, "check_test"), "Got plugin name"); - ok(!strcmp(temp_state_key->name, "funnykeyname"), "Got key name"); - - ok(!strcmp(temp_state_key->_filename, state_path), "Got internal filename"); - ok(temp_state_key->data_version == 54, "Version set"); - - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Got no state data as file does not exist"); - - /* - temp_fp = fopen("var/statefile", "r"); - if (temp_fp==NULL) - printf("Error opening. errno=%d\n", errno); - printf("temp_fp=%s\n", temp_fp); - ok( _np_state_read_file(temp_fp) == true, "Can read state file" ); - fclose(temp_fp); - */ - - temp_state_key->_filename = "var/statefile"; - temp_state_data = np_state_read(); - ok(this_monitoring_plugin->state->state_data != NULL, "Got state data now") || - diag("Are you running in right directory? Will get coredump next if not"); - ok(this_monitoring_plugin->state->state_data->time == 1234567890, "Got time"); - ok(!strcmp((char *)this_monitoring_plugin->state->state_data->data, "String to read"), "Data as expected"); - - temp_state_key->data_version = 53; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Older data version gives NULL"); - temp_state_key->data_version = 54; - - temp_state_key->_filename = "var/nonexistent"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Missing file gives NULL"); - ok(this_monitoring_plugin->state->state_data == NULL, "No state information"); - - temp_state_key->_filename = "var/oldformat"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Old file format gives NULL"); - - temp_state_key->_filename = "var/baddate"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Bad date gives NULL"); - - temp_state_key->_filename = "var/missingdataline"; - temp_state_data = np_state_read(); - ok(temp_state_data == NULL, "Missing data line gives NULL"); - - unlink("var/generated"); - temp_state_key->_filename = "var/generated"; - current_time = 1234567890; - np_state_write_string(current_time, "String to read"); - ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected"); - - unlink("var/generated_directory/statefile"); - unlink("var/generated_directory"); - temp_state_key->_filename = "var/generated_directory/statefile"; - current_time = 1234567890; - np_state_write_string(current_time, "String to read"); - ok(system("cmp var/generated_directory/statefile var/statefile") == 0, "Have created directory"); - - /* This test to check cannot write to dir - can't automate yet */ - /* - unlink("var/generated_bad_dir"); - mkdir("var/generated_bad_dir", S_IRUSR); - np_state_write_string(current_time, "String to read"); - */ - - temp_state_key->_filename = "var/generated"; - time(¤t_time); - np_state_write_string(0, "String to read"); - temp_state_data = np_state_read(); - /* Check time is set to current_time */ - ok(system("cmp var/generated var/statefile > /dev/null") != 0, "Generated file should be different this time"); - ok(this_monitoring_plugin->state->state_data->time - current_time <= 1, "Has time generated from current time"); - - /* Don't know how to automatically test this. Need to be able to redefine die and catch the error */ - /* - temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write"; - np_state_write_string(0, "Bad file"); - */ - - np_cleanup(); - - ok(this_monitoring_plugin == NULL, "Free'd this_monitoring_plugin"); - ok(mp_suid() == false, "Test aren't suid"); /* base states with random case */ char *states[] = {"Ok", "wArnINg", "cRiTIcaL", "UnKNoWN", NULL}; - for (i = 0; states[i] != NULL; i++) { - /* out of the random case states, create the lower and upper versions + numeric string one */ + for (int i = 0; states[i] != NULL; i++) { + /* out of the random case states, create the lower and upper versions + numeric string one + */ char *statelower = strdup(states[i]); char *stateupper = strdup(states[i]); char statenum[2]; - char *temp_ptr; - for (temp_ptr = statelower; *temp_ptr; temp_ptr++) { - *temp_ptr = tolower(*temp_ptr); + for (char *temp_ptr = statelower; *temp_ptr; temp_ptr++) { + *temp_ptr = (char)tolower(*temp_ptr); } - for (temp_ptr = stateupper; *temp_ptr; temp_ptr++) { - *temp_ptr = toupper(*temp_ptr); + for (char *temp_ptr = stateupper; *temp_ptr; temp_ptr++) { + *temp_ptr = (char)toupper(*temp_ptr); } snprintf(statenum, 2, "%i", i); /* Base test names, we'll append the state string */ char testname[64] = "Translate state string: "; - int tlen = strlen(testname); + size_t tlen = strlen(testname); strcpy(testname + tlen, states[i]); ok(i == mp_translate_state(states[i]), testname); diff --git a/lib/utils_base.c b/lib/utils_base.c index 43e88e7a..29b393d0 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -74,14 +74,6 @@ void np_set_args(int argc, char **argv) { void np_cleanup(void) { if (this_monitoring_plugin != NULL) { - if (this_monitoring_plugin->state != NULL) { - if (this_monitoring_plugin->state->state_data) { - np_free(this_monitoring_plugin->state->state_data->data); - np_free(this_monitoring_plugin->state->state_data); - } - np_free(this_monitoring_plugin->state->name); - np_free(this_monitoring_plugin->state); - } np_free(this_monitoring_plugin->plugin_name); np_free(this_monitoring_plugin); } @@ -435,339 +427,3 @@ int mp_translate_state(char *state_text) { } return ERROR; } - -/* - * Returns a string to use as a keyname, based on an md5 hash of argv, thus - * hopefully a unique key per service/plugin invocation. Use the extra-opts - * parse of argv, so that uniqueness in parameters are reflected there. - */ -char *_np_state_generate_key(void) { - char **argv = this_monitoring_plugin->argv; - unsigned char result[256]; - -#ifdef USE_OPENSSL - /* - * This code path is chosen if openssl is available (which should be the most common - * scenario). Alternatively, the gnulib implementation/ - * - */ - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - - EVP_DigestInit(ctx, EVP_sha256()); - - for (int i = 0; i < this_monitoring_plugin->argc; i++) { - EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); - } - - EVP_DigestFinal(ctx, result, NULL); -#else - - struct sha256_ctx ctx; - - for (int i = 0; i < this_monitoring_plugin->argc; i++) { - sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); - } - - sha256_finish_ctx(&ctx, result); -#endif // FOUNDOPENSSL - - char keyname[41]; - for (int i = 0; i < 20; ++i) { - sprintf(&keyname[2 * i], "%02x", result[i]); - } - - keyname[40] = '\0'; - - char *keyname_copy = strdup(keyname); - if (keyname_copy == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - - return keyname_copy; -} - -void _cleanup_state_data(void) { - if (this_monitoring_plugin->state->state_data != NULL) { - np_free(this_monitoring_plugin->state->state_data->data); - np_free(this_monitoring_plugin->state->state_data); - } -} - -/* - * Internal function. Returns either: - * envvar NAGIOS_PLUGIN_STATE_DIRECTORY - * statically compiled shared state directory - */ -char *_np_state_calculate_location_prefix(void) { - char *env_dir; - - /* Do not allow passing MP_STATE_PATH in setuid plugins - * for security reasons */ - if (!mp_suid()) { - env_dir = getenv("MP_STATE_PATH"); - if (env_dir && env_dir[0] != '\0') { - return env_dir; - } - /* This is the former ENV, for backward-compatibility */ - env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); - if (env_dir && env_dir[0] != '\0') { - return env_dir; - } - } - - return NP_STATE_DIR_PREFIX; -} - -/* - * Initiatializer for state routines. - * Sets variables. Generates filename. Returns np_state_key. die with - * UNKNOWN if exception - */ -void np_enable_state(char *keyname, int expected_data_version) { - if (this_monitoring_plugin == NULL) { - die(STATE_UNKNOWN, _("This requires np_init to be called")); - } - - state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); - if (this_state == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - char *temp_keyname = NULL; - if (keyname == NULL) { - temp_keyname = _np_state_generate_key(); - } else { - temp_keyname = strdup(keyname); - if (temp_keyname == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - } - - /* Die if invalid characters used for keyname */ - char *tmp_char = temp_keyname; - while (*tmp_char != '\0') { - if (!(isalnum(*tmp_char) || *tmp_char == '_')) { - die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); - } - tmp_char++; - } - this_state->name = temp_keyname; - this_state->plugin_name = this_monitoring_plugin->plugin_name; - this_state->data_version = expected_data_version; - this_state->state_data = NULL; - - /* Calculate filename */ - char *temp_filename = NULL; - int error = - asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), - (unsigned long)geteuid(), this_monitoring_plugin->plugin_name, this_state->name); - if (error < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - this_state->_filename = temp_filename; - - this_monitoring_plugin->state = this_state; -} - -/* - * Will return NULL if no data is available (first run). If key currently - * exists, read data. If state file format version is not expected, return - * as if no data. Get state data version number and compares to expected. - * If numerically lower, then return as no previous state. die with UNKNOWN - * if exceptional error. - */ -state_data *np_state_read(void) { - if (this_monitoring_plugin == NULL) { - die(STATE_UNKNOWN, _("This requires np_init to be called")); - } - - bool error_code = false; - - /* Open file. If this fails, no previous state found */ - FILE *statefile = fopen(this_monitoring_plugin->state->_filename, "r"); - if (statefile != NULL) { - - state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); - if (this_state_data == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - this_state_data->data = NULL; - this_monitoring_plugin->state->state_data = this_state_data; - - error_code = _np_state_read_file(statefile); - - fclose(statefile); - } - - if (!error_code) { - _cleanup_state_data(); - } - - return this_monitoring_plugin->state->state_data; -} - -/* - * Read the state file - */ -bool _np_state_read_file(FILE *state_file) { - time_t current_time; - time(¤t_time); - - /* Note: This introduces a limit of 1024 bytes in the string data */ - char *line = (char *)calloc(1, 1024); - if (line == NULL) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - bool status = false; - enum { - STATE_FILE_VERSION, - STATE_DATA_VERSION, - STATE_DATA_TIME, - STATE_DATA_TEXT, - STATE_DATA_END - } expected = STATE_FILE_VERSION; - - int failure = 0; - while (!failure && (fgets(line, 1024, state_file)) != NULL) { - size_t pos = strlen(line); - if (line[pos - 1] == '\n') { - line[pos - 1] = '\0'; - } - - if (line[0] == '#') { - continue; - } - - switch (expected) { - case STATE_FILE_VERSION: { - int i = atoi(line); - if (i != NP_STATE_FORMAT_VERSION) { - failure++; - } else { - expected = STATE_DATA_VERSION; - } - } break; - case STATE_DATA_VERSION: { - int i = atoi(line); - if (i != this_monitoring_plugin->state->data_version) { - failure++; - } else { - expected = STATE_DATA_TIME; - } - } break; - case STATE_DATA_TIME: { - /* If time > now, error */ - time_t data_time = strtoul(line, NULL, 10); - if (data_time > current_time) { - failure++; - } else { - this_monitoring_plugin->state->state_data->time = data_time; - expected = STATE_DATA_TEXT; - } - } break; - case STATE_DATA_TEXT: - this_monitoring_plugin->state->state_data->data = strdup(line); - if (this_monitoring_plugin->state->state_data->data == NULL) { - die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); - } - expected = STATE_DATA_END; - status = true; - break; - case STATE_DATA_END:; - } - } - - np_free(line); - return status; -} - -/* - * If time=NULL, use current time. Create state file, with state format - * version, default text. Writes version, time, and data. Avoid locking - * problems - use mv to write and then swap. Possible loss of state data if - * two things writing to same key at same time. - * Will die with UNKNOWN if errors - */ -void np_state_write_string(time_t data_time, char *data_string) { - time_t current_time; - if (data_time == 0) { - time(¤t_time); - } else { - current_time = data_time; - } - - int result = 0; - - /* If file doesn't currently exist, create directories */ - if (access(this_monitoring_plugin->state->_filename, F_OK) != 0) { - char *directories = NULL; - result = asprintf(&directories, "%s", this_monitoring_plugin->state->_filename); - if (result < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - for (char *p = directories + 1; *p; p++) { - if (*p == '/') { - *p = '\0'; - if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { - /* Can't free this! Otherwise error message is wrong! */ - /* np_free(directories); */ - die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); - } - *p = '/'; - } - } - np_free(directories); - } - - char *temp_file = NULL; - result = asprintf(&temp_file, "%s.XXXXXX", this_monitoring_plugin->state->_filename); - if (result < 0) { - die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); - } - - int temp_file_desc = 0; - if ((temp_file_desc = mkstemp(temp_file)) == -1) { - np_free(temp_file); - die(STATE_UNKNOWN, _("Cannot create temporary filename")); - } - - FILE *temp_file_pointer = (FILE *)fdopen(temp_file_desc, "w"); - if (temp_file_pointer == NULL) { - close(temp_file_desc); - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Unable to open temporary state file")); - } - - fprintf(temp_file_pointer, "# NP State file\n"); - fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); - fprintf(temp_file_pointer, "%d\n", this_monitoring_plugin->state->data_version); - fprintf(temp_file_pointer, "%lu\n", current_time); - fprintf(temp_file_pointer, "%s\n", data_string); - - fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); - - fflush(temp_file_pointer); - - result = fclose(temp_file_pointer); - - fsync(temp_file_desc); - - if (result != 0) { - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Error writing temp file")); - } - - if (rename(temp_file, this_monitoring_plugin->state->_filename) != 0) { - unlink(temp_file); - np_free(temp_file); - die(STATE_UNKNOWN, _("Cannot rename state temp file")); - } - - np_free(temp_file); -} diff --git a/lib/utils_base.h b/lib/utils_base.h index 123066f8..f1c99a54 100644 --- a/lib/utils_base.h +++ b/lib/utils_base.h @@ -8,7 +8,6 @@ #include "./perfdata.h" #include "./thresholds.h" - #ifndef USE_OPENSSL # include "sha256.h" #endif @@ -26,25 +25,8 @@ #define OUTSIDE 0 #define INSIDE 1 -#define NP_STATE_FORMAT_VERSION 1 - -typedef struct state_data_struct { - time_t time; - void *data; - int length; /* Of binary data */ -} state_data; - -typedef struct state_key_struct { - char *name; - char *plugin_name; - int data_version; - char *_filename; - state_data *state_data; -} state_key; - typedef struct np_struct { char *plugin_name; - state_key *state; int argc; char **argv; } monitoring_plugin; @@ -100,10 +82,6 @@ char *np_extract_value(const char *, const char *, char); */ int mp_translate_state(char *); -void np_enable_state(char *, int); -state_data *np_state_read(void); -void np_state_write_string(time_t, char *); - void np_init(char *, int argc, char **argv); void np_set_args(int argc, char **argv); void np_cleanup(void); diff --git a/plugins/Makefile.am b/plugins/Makefile.am index f2f1777f..deae938d 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -13,8 +13,14 @@ AM_CFLAGS = -DNP_VERSION='"$(NP_VERSION)"' VPATH = $(top_srcdir) $(top_srcdir)/lib $(top_srcdir)/plugins $(top_srcdir)/plugins/t -AM_CPPFLAGS = -I.. -I$(top_srcdir)/lib -I$(top_srcdir)/gl -I$(top_srcdir)/intl \ - @LDAPINCLUDE@ @PGINCLUDE@ @SSLINCLUDE@ +AM_CPPFLAGS = -I.. \ + -I$(top_srcdir)/lib \ + -I$(top_srcdir)/gl \ + -I$(top_srcdir)/intl \ + -DNP_STATE_DIR_PREFIX=\"$(localstatedir)\" \ + @LDAPINCLUDE@ \ + @PGINCLUDE@ \ + @SSLINCLUDE@ localedir = $(datadir)/locale # gettext docs say to use AM_CPPFLAGS, but per module_CPPFLAGS override this @@ -42,11 +48,13 @@ EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_hpjd \ check_procs check_mysql_query check_apt check_dbi check_curl \ \ tests/test_check_swap \ + tests/test_check_snmp \ tests/test_check_disk SUBDIRS = picohttpparser np_test_scripts = tests/test_check_swap.t \ + tests/test_check_snmp.t \ tests/test_check_disk.t EXTRA_DIST = t \ @@ -178,6 +186,8 @@ 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 +tests_test_check_snmp_LDADD = $(BASEOBJS) $(tap_ldflags) -ltap +tests_test_check_snmp_SOURCES = tests/test_check_snmp.c check_snmp.d/check_snmp_helpers.c tests_test_check_disk_LDADD = $(BASEOBJS) $(tap_ldflags) check_disk.d/utils_disk.c -ltap tests_test_check_disk_SOURCES = tests/test_check_disk.c diff --git a/plugins/check_snmp.c b/plugins/check_snmp.c index 3c054259..a5a7afe8 100644 --- a/plugins/check_snmp.c +++ b/plugins/check_snmp.c @@ -37,10 +37,8 @@ const char *email = "devel@monitoring-plugins.org"; #include "./utils.h" #include "../lib/states.h" -#include "../lib/thresholds.h" #include "../lib/utils_base.h" #include "../lib/output.h" -#include "../lib/perfdata.h" #include "check_snmp.d/check_snmp_helpers.h" #include @@ -49,6 +47,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include "check_snmp.d/config.h" +#include #include #include #include @@ -63,6 +62,7 @@ const char *email = "devel@monitoring-plugins.org"; #include #include #include "../gl/regex.h" +#include "../gl/base64.h" #include const char DEFAULT_COMMUNITY[] = "public"; @@ -86,7 +86,168 @@ static char *get_next_argument(char *str); void print_usage(void); void print_help(void); -static int verbose = 0; +int verbose = 0; + +typedef struct { + int errorcode; + char *state_string; +} gen_state_string_type; +gen_state_string_type gen_state_string(check_snmp_state_entry *entries, size_t num_of_entries) { + char *encoded_string = NULL; + gen_state_string_type result = {.errorcode = OK, .state_string = NULL}; + + if (verbose > 1) { + printf("%s:\n", __FUNCTION__); + for (size_t i = 0; i < num_of_entries; i++) { + printf("Entry timestamp %lu: %s", entries[i].timestamp, ctime(&entries[i].timestamp)); + switch (entries[i].type) { + case ASN_GAUGE: + printf("Type GAUGE\n"); + break; + case ASN_TIMETICKS: + printf("Type TIMETICKS\n"); + break; + case ASN_COUNTER: + printf("Type COUNTER\n"); + break; + case ASN_UINTEGER: + printf("Type UINTEGER\n"); + break; + case ASN_COUNTER64: + printf("Type COUNTER64\n"); + break; + case ASN_FLOAT: + printf("Type FLOAT\n"); + case ASN_DOUBLE: + printf("Type DOUBLE\n"); + break; + case ASN_INTEGER: + printf("Type INTEGER\n"); + break; + } + + switch (entries[i].type) { + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: + case ASN_COUNTER64: + printf("Value %llu\n", entries[i].value.uIntVal); + break; + case ASN_FLOAT: + case ASN_DOUBLE: + printf("Value %f\n", entries[i].value.doubleVal); + break; + case ASN_INTEGER: + printf("Value %lld\n", entries[i].value.intVal); + break; + } + } + } + + idx_t encoded = base64_encode_alloc((const char *)entries, + (idx_t)(num_of_entries * sizeof(check_snmp_state_entry)), + &encoded_string); + + if (encoded > 0 && encoded_string != NULL) { + // success + if (verbose > 1) { + printf("encoded string: %s\n", encoded_string); + printf("encoded string length: %lu\n", strlen(encoded_string)); + } + result.state_string = encoded_string; + return result; + } + result.errorcode = ERROR; + return result; +} + +typedef struct { + int errorcode; + check_snmp_state_entry *state; +} recover_state_data_type; +recover_state_data_type recover_state_data(char *state_string, idx_t state_string_length) { + recover_state_data_type result = {.errorcode = OK, .state = NULL}; + + if (verbose > 1) { + printf("%s:\n", __FUNCTION__); + printf("State string: %s\n", state_string); + printf("State string length: %lu\n", state_string_length); + } + + idx_t outlen = 0; + bool decoded = + base64_decode_alloc(state_string, state_string_length, (char **)&result.state, &outlen); + + if (!decoded) { + if (verbose) { + printf("Failed to decode state string\n"); + } + // failure to decode + result.errorcode = ERROR; + return result; + } + + if (result.state == NULL) { + // Memory Error? + result.errorcode = ERROR; + return result; + } + + if (verbose > 1) { + printf("Recovered %lu entries of size %lu\n", + (size_t)outlen / sizeof(check_snmp_state_entry), outlen); + + for (size_t i = 0; i < (size_t)outlen / sizeof(check_snmp_state_entry); i++) { + printf("Entry timestamp %lu: %s", result.state[i].timestamp, + ctime(&result.state[i].timestamp)); + switch (result.state[i].type) { + case ASN_GAUGE: + printf("Type GAUGE\n"); + break; + case ASN_TIMETICKS: + printf("Type TIMETICKS\n"); + break; + case ASN_COUNTER: + printf("Type COUNTER\n"); + break; + case ASN_UINTEGER: + printf("Type UINTEGER\n"); + break; + case ASN_COUNTER64: + printf("Type COUNTER64\n"); + break; + case ASN_FLOAT: + printf("Type FLOAT\n"); + case ASN_DOUBLE: + printf("Type DOUBLE\n"); + break; + case ASN_INTEGER: + printf("Type INTEGER\n"); + break; + } + + switch (result.state[i].type) { + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: + case ASN_COUNTER64: + printf("Value %llu\n", result.state[i].value.uIntVal); + break; + case ASN_FLOAT: + case ASN_DOUBLE: + printf("Value %f\n", result.state[i].value.doubleVal); + break; + case ASN_INTEGER: + printf("Value %lld\n", result.state[i].value.intVal); + break; + } + } + } + + return result; +} int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -97,6 +258,8 @@ int main(int argc, char **argv) { np_init((char *)progname, argc, argv); + state_key stateKey = np_enable_state(NULL, 1, progname, argc, argv); + /* Parse extra opts if any */ argv = np_extra_opts(&argc, argv, progname); @@ -105,9 +268,6 @@ int main(int argc, char **argv) { // Initialize net-snmp before touching the session we are going to use init_snmp("check_snmp"); - time_t current_time; - time(¤t_time); - process_arguments_wrapper paw_tmp = process_arguments(argc, argv); if (paw_tmp.errorcode == ERROR) { usage4(_("Could not parse arguments")); @@ -119,347 +279,103 @@ int main(int argc, char **argv) { mp_set_format(config.output_format); } - if (config.ignore_mib_parsing_errors) { - char *opt_toggle_res = snmp_mib_toggle_options("e"); - if (opt_toggle_res != NULL) { - die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); - } - } - - struct snmp_pdu *pdu = NULL; - if (config.use_getnext) { - pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); - } else { - pdu = snmp_pdu_create(SNMP_MSG_GET); - } - - for (size_t i = 0; i < config.num_of_test_units; i++) { - assert(config.test_units[i].oid != NULL); - if (verbose > 0) { - printf("OID %zu to parse: %s\n", i, config.test_units[i].oid); - } - - oid tmp_OID[MAX_OID_LEN]; - size_t tmp_OID_len = MAX_OID_LEN; - if (snmp_parse_oid(config.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { - // success - snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); - } else { - // failed - snmp_perror("Parsing failure"); - die(STATE_UNKNOWN, "Failed to parse OID\n"); - } - } - /* Set signal handling and alarm */ if (signal(SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) { usage4(_("Cannot catch SIGALRM")); } - const int timeout_safety_tolerance = 5; - alarm((timeout_interval * (unsigned int)config.snmp_session.retries) + - timeout_safety_tolerance); - - struct snmp_session *active_session = snmp_open(&config.snmp_session); - if (active_session == NULL) { - int pcliberr = 0; - int psnmperr = 0; - char *pperrstring = NULL; - snmp_error(&config.snmp_session, &pcliberr, &psnmperr, &pperrstring); - die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); - } - - struct snmp_pdu *response = NULL; - int snmp_query_status = snmp_synch_response(active_session, pdu, &response); - - if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { - int pcliberr = 0; - int psnmperr = 0; - char *pperrstring = NULL; - snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); + time_t current_time; + time(¤t_time); - if (psnmperr == SNMPERR_TIMEOUT) { - // We exit with critical here for some historical reason - die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); - } - die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); + if (verbose > 2) { + printf("current time: %s (timestamp: %lu)\n", ctime(¤t_time), current_time); } - snmp_close(active_session); - - /* disable alarm again */ - alarm(0); + snmp_responces response = do_snmp_query(config.snmp_params); mp_check overall = mp_check_init(); - mp_subcheck sc_successfull_query = mp_subcheck_init(); - xasprintf(&sc_successfull_query.output, "SNMP query was successful"); - sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); - mp_add_subcheck_to_check(&overall, sc_successfull_query); - - // We got the the query results, now process them - size_t loop_index = 0; - for (netsnmp_variable_list *vars = response->variables; vars; - vars = vars->next_variable, loop_index++) { - mp_subcheck sc_oid_test = mp_subcheck_init(); + if (response.errorcode == OK) { + mp_subcheck sc_successfull_query = mp_subcheck_init(); + xasprintf(&sc_successfull_query.output, "SNMP query was successful"); + sc_successfull_query = mp_set_subcheck_state(sc_successfull_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_successfull_query); + } else { + // Error treatment here, either partial or whole + mp_subcheck sc_failed_query = mp_subcheck_init(); + xasprintf(&sc_failed_query.output, "SNMP query failed"); + sc_failed_query = mp_set_subcheck_state(sc_failed_query, STATE_OK); + mp_add_subcheck_to_check(&overall, sc_failed_query); + mp_exit(overall); + } - if (verbose > 0) { - printf("loop_index: %zu\n", loop_index); - } + check_snmp_state_entry *prev_state = NULL; + bool have_previous_state = false; - if ((config.test_units[loop_index].label != NULL) && - (strcmp(config.test_units[loop_index].label, "") != 0)) { - xasprintf(&sc_oid_test.output, "%s - ", config.test_units[loop_index].label); + if (config.evaluation_params.calculate_rate) { + state_data *previous_state = np_state_read(stateKey); + if (previous_state == NULL) { + // failed to recover state + // or no previous state + have_previous_state = false; } else { - sc_oid_test.output = strdup(""); - } - - char oid_string[(MAX_OID_LEN * 2) + 1] = {}; - - int oid_string_result = - snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, vars->name, vars->name_length); - if (oid_string_result <= 0) { - // TODO error here - } - - if (verbose > 2) { - printf("Processing oid %s\n", oid_string); - } + // sanity check + recover_state_data_type prev_state_wrapper = + recover_state_data(previous_state->data, (idx_t)previous_state->length); - mp_perfdata_value pd_result_val = {0}; - xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); - sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); - - switch (vars->type) { - case ASN_OCTET_STR: { - if (verbose) { - printf("Debug: Got a string\n"); - } - - char *tmp = (char *)vars->val.string; - - if (strchr(tmp, '"') != NULL) { - // got double quote in the string - if (strchr(tmp, '\'') != NULL) { - // got single quote in the string too - // dont quote that at all to avoid even more confusion - xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); - } else { - // quote with single quotes - xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); - } + if (prev_state_wrapper.errorcode == OK) { + have_previous_state = true; + prev_state = prev_state_wrapper.state; } else { - // quote with double quotes - xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); - } - - if (strlen(tmp) == 0) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, config.nulloid_result); + have_previous_state = false; + prev_state = NULL; } - - // String matching test - if ((config.test_units[loop_index].eval_mthd.crit_string)) { - if (strcmp(tmp, config.string_cmp_value)) { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_CRITICAL : STATE_OK); - } else { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); - } - } else if (config.test_units[loop_index].eval_mthd.crit_regex) { - const size_t nmatch = config.regex_cmp_value.re_nsub + 1; - regmatch_t pmatch[nmatch]; - memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); - - int excode = regexec(&config.regex_cmp_value, tmp, nmatch, pmatch, 0); - if (excode == 0) { - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, (config.invert_search) ? STATE_OK : STATE_CRITICAL); - } else if (excode != REG_NOMATCH) { - char errbuf[MAX_INPUT_BUFFER] = ""; - regerror(excode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); - printf(_("Execute Error: %s\n"), errbuf); - exit(STATE_CRITICAL); - } else { // REG_NOMATCH - sc_oid_test = mp_set_subcheck_state( - sc_oid_test, config.invert_search ? STATE_CRITICAL : STATE_OK); - } - } - - mp_add_subcheck_to_check(&overall, sc_oid_test); } - continue; - case ASN_OPAQUE: - if (verbose) { - printf("Debug: Got OPAQUE\n"); - } - break; - case ASN_COUNTER64: { - if (verbose) { - printf("Debug: Got counter64\n"); - } - struct counter64 tmp = *(vars->val.counter64); - uint64_t counter = (tmp.high << 32) + tmp.low; - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)counter * config.multiplier; - } - - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(counter); - } - - } break; - /* Numerical values */ - case ASN_GAUGE: // same as ASN_UNSIGNED - case ASN_TIMETICKS: - case ASN_COUNTER: - case ASN_UINTEGER: { - if (verbose) { - printf("Debug: Got a Integer like\n"); - } - unsigned long tmp = (unsigned long)*(vars->val.integer); - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)tmp * config.multiplier; - } + } - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(tmp); - } - break; + check_snmp_state_entry *new_state = NULL; + if (config.evaluation_params.calculate_rate) { + new_state = calloc(config.snmp_params.num_of_test_units, sizeof(check_snmp_state_entry)); + if (new_state == NULL) { + die(STATE_UNKNOWN, "memory allocation failed"); } - case ASN_INTEGER: { - if (verbose) { - printf("Debug: Got a Integer\n"); - } - - long tmp = *(vars->val.integer); - - if (config.multiplier_set || config.offset_set) { - double processed = 0; - if (config.multiplier_set) { - processed = (double)tmp * config.multiplier; - } - - if (config.offset_set) { - processed += config.offset; - } - pd_result_val = mp_create_pd_value(processed); - } else { - pd_result_val = mp_create_pd_value(tmp); - } - - } break; - case ASN_FLOAT: { - if (verbose) { - printf("Debug: Got a float\n"); - } - double tmp = *(vars->val.floatVal); - - if (config.multiplier_set) { - tmp *= config.multiplier; - } - - if (config.offset_set) { - tmp += config.offset; - } + } - pd_result_val = mp_create_pd_value(tmp); - break; + // We got the the query results, now process them + for (size_t loop_index = 0; loop_index < config.snmp_params.num_of_test_units; loop_index++) { + if (verbose > 0) { + printf("loop_index: %zu\n", loop_index); } - case ASN_DOUBLE: { - if (verbose) { - printf("Debug: Got a double\n"); - } - double tmp = *(vars->val.doubleVal); - if (config.multiplier_set) { - tmp *= config.multiplier; - } - - if (config.offset_set) { - tmp += config.offset; - } - pd_result_val = mp_create_pd_value(tmp); - break; + check_snmp_state_entry previous_unit_state = {}; + if (config.evaluation_params.calculate_rate && have_previous_state) { + previous_unit_state = prev_state[loop_index]; } - case ASN_IPADDRESS: - if (verbose) { - printf("Debug: Got an IP address\n"); - } - continue; - default: - if (verbose) { - printf("Debug: Got a unmatched result type: %hhu\n", vars->type); - } - // TODO: Error here? - continue; - } - - // some kind of numerical value - mp_perfdata pd_num_val = { - .value = pd_result_val, - }; - if (!config.use_perf_data_labels_from_input) { - // Use oid for perdata label - pd_num_val.label = strdup(oid_string); - // TODO strdup error checking - } else if (config.test_units[loop_index].label != NULL && - strcmp(config.test_units[loop_index].label, "") != 0) { - pd_num_val.label = config.test_units[loop_index].label; - } else { - pd_num_val.label = config.test_units[loop_index].oid; - } + check_snmp_evaluation single_eval = + evaluate_single_unit(response.response_values[loop_index], config.evaluation_params, + config.snmp_params.test_units[loop_index], current_time, + previous_unit_state, have_previous_state); - if (config.test_units[loop_index].unit_value != NULL && - strcmp(config.test_units[loop_index].unit_value, "") != 0) { - pd_num_val.uom = config.test_units[loop_index].unit_value; + if (config.evaluation_params.calculate_rate && + mp_compute_subcheck_state(single_eval.sc) != STATE_UNKNOWN) { + new_state[loop_index] = single_eval.state; } - xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, - pd_value_to_string(pd_result_val)); + mp_add_subcheck_to_check(&overall, single_eval.sc); + } - if (config.test_units[loop_index].unit_value != NULL && - strcmp(config.test_units[loop_index].unit_value, "") != 0) { - xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, - config.test_units[loop_index].unit_value); - } + if (config.evaluation_params.calculate_rate) { + // store state + gen_state_string_type current_state_wrapper = + gen_state_string(new_state, config.snmp_params.num_of_test_units); - if (config.test_units[loop_index].threshold.warning_is_set || - config.test_units[loop_index].threshold.critical_is_set) { - pd_num_val = mp_pd_set_thresholds(pd_num_val, config.test_units[loop_index].threshold); - mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); - - if (tmp_state == STATE_WARNING) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); - xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", - sc_oid_test.output); - } else if (tmp_state == STATE_CRITICAL) { - sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); - xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", - sc_oid_test.output); - } + if (current_state_wrapper.errorcode == OK) { + np_state_write_string(stateKey, current_time, current_state_wrapper.state_string); + } else { + die(STATE_UNKNOWN, "failed to create state string"); } - - mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); - - mp_add_subcheck_to_check(&overall, sc_oid_test); } - mp_exit(overall); } @@ -472,6 +388,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { ignore_mib_parsing_errors_index, connection_prefix_index, output_format_index, + calculate_rate, + rate_multiplier }; static struct option longopts[] = { @@ -510,6 +428,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { {"ignore-mib-parsing-errors", no_argument, 0, ignore_mib_parsing_errors_index}, {"connection-prefix", required_argument, 0, connection_prefix_index}, {"output-format", required_argument, 0, output_format_index}, + {"rate", no_argument, 0, calculate_rate}, + {"rate-multiplier", required_argument, 0, rate_multiplier}, {0, 0, 0, 0}}; if (argc < 2) { @@ -575,8 +495,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } check_snmp_config config = check_snmp_config_init(); - config.test_units = tmp; - config.num_of_test_units = oid_counter; + config.snmp_params.test_units = tmp; + config.snmp_params.num_of_test_units = oid_counter; option = 0; optind = 1; // Reset argument scanner @@ -616,11 +536,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Connection info */ case 'C': /* group or community */ - config.snmp_session.community = (unsigned char *)optarg; - config.snmp_session.community_len = strlen(optarg); + config.snmp_params.snmp_session.community = (unsigned char *)optarg; + config.snmp_params.snmp_session.community_len = strlen(optarg); break; case 'H': /* Host or server */ - config.snmp_session.peername = optarg; + config.snmp_params.snmp_session.peername = optarg; break; case 'p': /*port number */ // Add port to "peername" below to not rely on argument order @@ -630,15 +550,15 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { miblist = optarg; break; case 'n': /* use_getnext instead of get */ - config.use_getnext = true; + config.snmp_params.use_getnext = true; break; case 'P': /* SNMP protocol version */ if (strcasecmp("1", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_1; + config.snmp_params.snmp_session.version = SNMP_VERSION_1; } else if (strcasecmp("2c", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_2c; + config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } else if (strcasecmp("3", optarg) == 0) { - config.snmp_session.version = SNMP_VERSION_3; + config.snmp_params.snmp_session.version = SNMP_VERSION_3; } else { die(STATE_UNKNOWN, "invalid SNMP version/protocol: %s", optarg); } @@ -646,45 +566,51 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'N': /* SNMPv3 context name */ - config.snmp_session.contextName = optarg; - config.snmp_session.contextNameLen = strlen(optarg); + config.snmp_params.snmp_session.contextName = optarg; + config.snmp_params.snmp_session.contextNameLen = strlen(optarg); break; case 'L': /* security level */ if (strcasecmp("noAuthNoPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; } else if (strcasecmp("authNoPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; } else if (strcasecmp("authPriv", optarg) == 0) { - config.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; + config.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; } else { die(STATE_UNKNOWN, "invalid security level: %s", optarg); } break; case 'U': /* security username */ - config.snmp_session.securityName = optarg; - config.snmp_session.securityNameLen = strlen(optarg); + config.snmp_params.snmp_session.securityName = optarg; + config.snmp_params.snmp_session.securityNameLen = strlen(optarg); break; case 'a': /* auth protocol */ // SNMPv3: SHA or MD5 // TODO Test for availability of individual protocols if (strcasecmp("MD5", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACMD5AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMACMD5AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMACMD5AuthProtocol); } else if (strcasecmp("SHA", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMACSHA1AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMACSHA1AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMACSHA1AuthProtocol); } else if (strcasecmp("SHA224", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC128SHA224AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC128SHA224AuthProtocol); } else if (strcasecmp("SHA256", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC192SHA256AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC192SHA256AuthProtocol); } else if (strcasecmp("SHA384", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC256SHA384AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC256SHA384AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC256SHA384AuthProtocol); } else if (strcasecmp("SHA512", optarg) == 0) { - config.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmHMAC384SHA512AuthProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmHMAC384SHA512AuthProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmHMAC384SHA512AuthProtocol); } else { die(STATE_UNKNOWN, "Unknown authentication protocol"); } @@ -692,24 +618,28 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { case 'x': /* priv protocol */ if (strcasecmp("DES", optarg) == 0) { #ifdef HAVE_USM_DES_PRIV_PROTOCOL - config.snmp_session.securityAuthProto = usmDESPrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmDESPrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmDESPrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmDESPrivProtocol); #else die(STATE_UNKNOWN, "DES Privacy Protocol not available on this platform"); #endif } else if (strcasecmp("AES", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAESPrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAESPrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAESPrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAESPrivProtocol); // } else if (strcasecmp("AES128", optarg)) { // config.snmp_session.securityAuthProto = usmAES128PrivProtocol; // config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES128PrivProtocol) // / OID_LENGTH(oid); } else if (strcasecmp("AES192", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAES192PrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES192PrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAES192PrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAES192PrivProtocol); } else if (strcasecmp("AES256", optarg) == 0) { - config.snmp_session.securityAuthProto = usmAES256PrivProtocol; - config.snmp_session.securityAuthProtoLen = OID_LENGTH(usmAES256PrivProtocol); + config.snmp_params.snmp_session.securityAuthProto = usmAES256PrivProtocol; + config.snmp_params.snmp_session.securityAuthProtoLen = + OID_LENGTH(usmAES256PrivProtocol); // } else if (strcasecmp("AES192Cisco", optarg)) { // config.snmp_session.securityAuthProto = usmAES192CiscoPrivProtocol; // config.snmp_session.securityAuthProtoLen = @@ -738,7 +668,7 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (!is_integer(optarg)) { usage2(_("Retries interval must be a positive integer"), optarg); } else { - config.snmp_session.retries = atoi(optarg); + config.snmp_params.snmp_session.retries = atoi(optarg); } break; case 't': /* timeout period */ @@ -751,10 +681,10 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { /* Test parameters */ case 'c': /* critical threshold */ - check_snmp_set_thresholds(optarg, config.test_units, oid_counter, true); + check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, true); break; case 'w': /* warning threshold */ - check_snmp_set_thresholds(optarg, config.test_units, oid_counter, false); + check_snmp_set_thresholds(optarg, config.snmp_params.test_units, oid_counter, false); break; case 'o': /* object identifier */ if (strspn(optarg, "0123456789.,") != strlen(optarg)) { @@ -763,25 +693,27 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { * so we have a mib variable, rather than just an SNMP OID, * so we have to actually read the mib files */ - config.need_mibs = true; + config.snmp_params.need_mibs = true; } for (char *ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), tmp_oid_counter++) { - config.test_units[tmp_oid_counter].oid = strdup(ptr); + config.snmp_params.test_units[tmp_oid_counter].oid = strdup(ptr); } break; case 'z': /* Null OID Return Check */ if (!is_integer(optarg)) { usage2(_("Exit status must be a positive integer"), optarg); } else { - config.nulloid_result = atoi(optarg); + config.evaluation_params.nulloid_result = atoi(optarg); } break; case 's': /* string or substring */ - strncpy(config.string_cmp_value, optarg, sizeof(config.string_cmp_value) - 1); - config.string_cmp_value[sizeof(config.string_cmp_value) - 1] = 0; - config.test_units[eval_counter++].eval_mthd.crit_string = true; + strncpy(config.evaluation_params.string_cmp_value, optarg, + sizeof(config.evaluation_params.string_cmp_value) - 1); + config.evaluation_params + .string_cmp_value[sizeof(config.evaluation_params.string_cmp_value) - 1] = 0; + config.snmp_params.test_units[eval_counter++].eval_mthd.crit_string = true; break; case 'R': /* regex */ cflags = REG_ICASE; @@ -792,72 +724,73 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE; strncpy(regex_expect, optarg, sizeof(regex_expect) - 1); regex_expect[sizeof(regex_expect) - 1] = 0; - int errcode = regcomp(&config.regex_cmp_value, regex_expect, cflags); + int errcode = regcomp(&config.evaluation_params.regex_cmp_value, regex_expect, cflags); if (errcode != 0) { char errbuf[MAX_INPUT_BUFFER] = ""; - regerror(errcode, &config.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + regerror(errcode, &config.evaluation_params.regex_cmp_value, errbuf, + MAX_INPUT_BUFFER); printf("Could Not Compile Regular Expression: %s", errbuf); process_arguments_wrapper result = { .errorcode = ERROR, }; return result; } - config.test_units[eval_counter++].eval_mthd.crit_regex = true; + config.snmp_params.test_units[eval_counter++].eval_mthd.crit_regex = true; } break; case 'l': /* label */ { - if (labels_counter >= config.num_of_test_units) { + if (labels_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { - config.test_units[labels_counter].label = ptr + 1; + config.snmp_params.test_units[labels_counter].label = ptr + 1; } else { - config.test_units[labels_counter].label = ptr; + config.snmp_params.test_units[labels_counter].label = ptr; } while (ptr && (ptr = get_next_argument(ptr))) { labels_counter++; ptr = trim_whitespaces_and_check_quoting(ptr); if (ptr[0] == '\'') { - config.test_units[labels_counter].label = ptr + 1; + config.snmp_params.test_units[labels_counter].label = ptr + 1; } else { - config.test_units[labels_counter].label = ptr; + config.snmp_params.test_units[labels_counter].label = ptr; } } labels_counter++; } break; case 'u': /* units */ { - if (unitv_counter >= config.num_of_test_units) { + if (unitv_counter >= config.snmp_params.num_of_test_units) { break; } char *ptr = trim_whitespaces_and_check_quoting(optarg); if (ptr[0] == '\'') { - config.test_units[unitv_counter].unit_value = ptr + 1; + config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { - config.test_units[unitv_counter].unit_value = ptr; + config.snmp_params.test_units[unitv_counter].unit_value = ptr; } while (ptr && (ptr = get_next_argument(ptr))) { unitv_counter++; ptr = trim_whitespaces_and_check_quoting(ptr); if (ptr[0] == '\'') { - config.test_units[unitv_counter].unit_value = ptr + 1; + config.snmp_params.test_units[unitv_counter].unit_value = ptr + 1; } else { - config.test_units[unitv_counter].unit_value = ptr; + config.snmp_params.test_units[unitv_counter].unit_value = ptr; } } unitv_counter++; } break; case offset_index: - config.offset = strtod(optarg, NULL); - config.offset_set = true; + config.evaluation_params.offset = strtod(optarg, NULL); + config.evaluation_params.offset_set = true; break; case invert_search_index: - config.invert_search = false; + config.evaluation_params.invert_search = false; break; case 'O': - config.use_perf_data_labels_from_input = true; + config.evaluation_params.use_oid_as_perf_data_label = true; break; case '4': // The default, do something here to be exclusive to -6 instead of doing nothing? @@ -871,12 +804,12 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'M': if (strspn(optarg, "0123456789.,") == strlen(optarg)) { - config.multiplier = strtod(optarg, NULL); - config.multiplier_set = true; + config.evaluation_params.multiplier = strtod(optarg, NULL); + config.evaluation_params.multiplier_set = true; } break; case ignore_mib_parsing_errors_index: - config.ignore_mib_parsing_errors = true; + config.snmp_params.ignore_mib_parsing_errors = true; break; case 'f': // Deprecated format option for floating point values break; @@ -892,13 +825,22 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { config.output_format = parser.output_format; break; } + case calculate_rate: + config.evaluation_params.calculate_rate = true; + break; + case rate_multiplier: + if (!is_integer(optarg) || + ((config.evaluation_params.rate_multiplier = (unsigned int)atoi(optarg)) <= 0)) { + usage2(_("Rate multiplier must be a positive integer"), optarg); + } + break; default: die(STATE_UNKNOWN, "Unknown option"); } } - if (config.snmp_session.peername == NULL) { - config.snmp_session.peername = argv[optind]; + if (config.snmp_params.snmp_session.peername == NULL) { + config.snmp_params.snmp_session.peername = argv[optind]; } // Build true peername here if necessary @@ -908,7 +850,8 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { // The default, do nothing } else if (strcasecmp(connection_prefix, "tcp") == 0) { // use tcp/ipv4 - xasprintf(&config.snmp_session.peername, "tcp:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "tcp:%s", + config.snmp_params.snmp_session.peername); } else if (strcasecmp(connection_prefix, "tcp6") == 0 || strcasecmp(connection_prefix, "tcpv6") == 0 || strcasecmp(connection_prefix, "tcpipv6") == 0 || @@ -917,19 +860,23 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { strcasecmp(connection_prefix, "udpv6") == 0) { // Man page (or net-snmp) code says IPv6 addresses should be wrapped in [], but it // works anyway therefore do nothing here - xasprintf(&config.snmp_session.peername, "%s:%s", connection_prefix, - config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", connection_prefix, + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "tls") == 0) { // TODO: Anything else to do here? - xasprintf(&config.snmp_session.peername, "tls:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "tls:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "dtls") == 0) { // TODO: Anything else to do here? - xasprintf(&config.snmp_session.peername, "dtls:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "dtls:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "unix") == 0) { // TODO: Check whether this is a valid path? - xasprintf(&config.snmp_session.peername, "unix:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "unix:%s", + config.snmp_params.snmp_session.peername); } else if (strcmp(connection_prefix, "ipx") == 0) { - xasprintf(&config.snmp_session.peername, "ipx:%s", config.snmp_session.peername); + xasprintf(&config.snmp_params.snmp_session.peername, "ipx:%s", + config.snmp_params.snmp_session.peername); } else { // Don't know that prefix, die here die(STATE_UNKNOWN, "Unknown connection prefix"); @@ -937,17 +884,18 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } /* Check server_address is given */ - if (config.snmp_session.peername == NULL) { + if (config.snmp_params.snmp_session.peername == NULL) { die(STATE_UNKNOWN, _("No host specified\n")); } if (port != NULL) { - xasprintf(&config.snmp_session.peername, "%s:%s", config.snmp_session.peername, port); + xasprintf(&config.snmp_params.snmp_session.peername, "%s:%s", + config.snmp_params.snmp_session.peername, port); } /* check whether to load locally installed MIBS (CPU/disk intensive) */ if (miblist == NULL) { - if (config.need_mibs) { + if (config.snmp_params.need_mibs) { setenv("MIBLS", DEFAULT_MIBLIST, 1); } else { setenv("MIBLS", "NONE", 1); @@ -959,37 +907,37 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { } // Historical default is SNMP v2c - if (!snmp_version_set_explicitely && config.snmp_session.community != NULL) { - config.snmp_session.version = SNMP_VERSION_2c; + if (!snmp_version_set_explicitely && config.snmp_params.snmp_session.community != NULL) { + config.snmp_params.snmp_session.version = SNMP_VERSION_2c; } - if ((config.snmp_session.version == SNMP_VERSION_1) || - (config.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ - /* - config.numauthpriv = 2; - config.authpriv = calloc(config.numauthpriv, sizeof(char *)); - config.authpriv[0] = strdup("-c"); - config.authpriv[1] = strdup(community); - */ - } else if (config.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ + if ((config.snmp_params.snmp_session.version == SNMP_VERSION_1) || + (config.snmp_params.snmp_session.version == SNMP_VERSION_2c)) { /* snmpv1 or snmpv2c */ + /* + config.numauthpriv = 2; + config.authpriv = calloc(config.numauthpriv, sizeof(char *)); + config.authpriv[0] = strdup("-c"); + config.authpriv[1] = strdup(community); + */ + } else if (config.snmp_params.snmp_session.version == SNMP_VERSION_3) { /* snmpv3 args */ // generate keys for priv and auth here (if demanded) - if (config.snmp_session.securityName == NULL) { + if (config.snmp_params.snmp_session.securityName == NULL) { die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname"); } - switch (config.snmp_session.securityLevel) { + switch (config.snmp_params.snmp_session.securityLevel) { case SNMP_SEC_LEVEL_AUTHPRIV: { if (authpasswd == NULL) { die(STATE_UNKNOWN, "No authentication passphrase was given, but authorization was requested"); } // auth and priv - int priv_key_generated = - generate_Ku(config.snmp_session.securityPrivProto, - (unsigned int)config.snmp_session.securityPrivProtoLen, authpasswd, - strlen((const char *)authpasswd), config.snmp_session.securityPrivKey, - &config.snmp_session.securityPrivKeyLen); + int priv_key_generated = generate_Ku( + config.snmp_params.snmp_session.securityPrivProto, + (unsigned int)config.snmp_params.snmp_session.securityPrivProtoLen, authpasswd, + strlen((const char *)authpasswd), config.snmp_params.snmp_session.securityPrivKey, + &config.snmp_params.snmp_session.securityPrivKeyLen); if (priv_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); @@ -1000,11 +948,11 @@ static process_arguments_wrapper process_arguments(int argc, char **argv) { if (privpasswd == NULL) { die(STATE_UNKNOWN, "No privacy passphrase was given, but privacy was requested"); } - int auth_key_generated = - generate_Ku(config.snmp_session.securityAuthProto, - (unsigned int)config.snmp_session.securityAuthProtoLen, privpasswd, - strlen((const char *)privpasswd), config.snmp_session.securityAuthKey, - &config.snmp_session.securityAuthKeyLen); + int auth_key_generated = generate_Ku( + config.snmp_params.snmp_session.securityAuthProto, + (unsigned int)config.snmp_params.snmp_session.securityAuthProtoLen, privpasswd, + strlen((const char *)privpasswd), config.snmp_params.snmp_session.securityAuthKey, + &config.snmp_params.snmp_session.securityAuthKeyLen); if (auth_key_generated != SNMPERR_SUCCESS) { die(STATE_UNKNOWN, "Failed to generate privacy key"); diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 8f4bcb9c..9fa396d8 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -1,6 +1,15 @@ #include "./check_snmp_helpers.h" #include #include "../../lib/utils_base.h" +#include "config.h" +#include +#include "../utils.h" +#include "output.h" +#include "states.h" +#include +#include + +extern int verbose; check_snmp_test_unit check_snmp_test_unit_init() { check_snmp_test_unit tmp = { @@ -78,38 +87,848 @@ int check_snmp_set_thresholds(const char *threshold_string, check_snmp_test_unit const int DEFAULT_PROTOCOL = SNMP_VERSION_1; const char DEFAULT_OUTPUT_DELIMITER[] = " "; -const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 1024; +const int RANDOM_STATE_DATA_LENGTH_PREDICTION = 8192; check_snmp_config check_snmp_config_init() { check_snmp_config tmp = { - .use_getnext = false, + .snmp_params = + { + .use_getnext = false, + + .ignore_mib_parsing_errors = false, + .need_mibs = false, - .ignore_mib_parsing_errors = false, - .need_mibs = false, + .test_units = NULL, + .num_of_test_units = 0, + }, - .test_units = NULL, - .num_of_test_units = 0, + .evaluation_params = + { + .nulloid_result = STATE_UNKNOWN, // state to return if no result for query - .nulloid_result = STATE_UNKNOWN, // state to return if no result for query + .invert_search = true, + .regex_cmp_value = {}, + .string_cmp_value = "", - .invert_search = true, - .regex_cmp_value = {}, - .string_cmp_value = "", + .multiplier = 1.0, + .multiplier_set = false, + .offset = 0, + .offset_set = false, - .multiplier = 1.0, - .multiplier_set = false, - .offset = 0, - .offset_set = false, + .use_oid_as_perf_data_label = false, - .use_perf_data_labels_from_input = false, + .calculate_rate = false, + .rate_multiplier = 1, + }, }; - snmp_sess_init(&tmp.snmp_session); + snmp_sess_init(&tmp.snmp_params.snmp_session); - tmp.snmp_session.retries = DEFAULT_RETRIES; - tmp.snmp_session.version = DEFAULT_SNMP_VERSION; - tmp.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; - tmp.snmp_session.community = (unsigned char *)"public"; - tmp.snmp_session.community_len = strlen("public"); + tmp.snmp_params.snmp_session.retries = DEFAULT_RETRIES; + tmp.snmp_params.snmp_session.version = DEFAULT_SNMP_VERSION; + tmp.snmp_params.snmp_session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; + tmp.snmp_params.snmp_session.community = (unsigned char *)"public"; + tmp.snmp_params.snmp_session.community_len = strlen("public"); return tmp; } + +snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) { + if (parameters.ignore_mib_parsing_errors) { + char *opt_toggle_res = snmp_mib_toggle_options("e"); + if (opt_toggle_res != NULL) { + die(STATE_UNKNOWN, "Unable to disable MIB parsing errors"); + } + } + + struct snmp_pdu *pdu = NULL; + if (parameters.use_getnext) { + pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); + } else { + pdu = snmp_pdu_create(SNMP_MSG_GET); + } + + for (size_t i = 0; i < parameters.num_of_test_units; i++) { + assert(parameters.test_units[i].oid != NULL); + if (verbose > 0) { + printf("OID %zu to parse: %s\n", i, parameters.test_units[i].oid); + } + + oid tmp_OID[MAX_OID_LEN]; + size_t tmp_OID_len = MAX_OID_LEN; + if (snmp_parse_oid(parameters.test_units[i].oid, tmp_OID, &tmp_OID_len) != NULL) { + // success + snmp_add_null_var(pdu, tmp_OID, tmp_OID_len); + } else { + // failed + snmp_perror("Parsing failure"); + die(STATE_UNKNOWN, "Failed to parse OID\n"); + } + } + + const int timeout_safety_tolerance = 5; + alarm((timeout_interval * (unsigned int)parameters.snmp_session.retries) + + timeout_safety_tolerance); + + struct snmp_session *active_session = snmp_open(¶meters.snmp_session); + if (active_session == NULL) { + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error(¶meters.snmp_session, &pcliberr, &psnmperr, &pperrstring); + die(STATE_UNKNOWN, "Failed to open SNMP session: %s\n", pperrstring); + } + + struct snmp_pdu *response = NULL; + int snmp_query_status = snmp_synch_response(active_session, pdu, &response); + + if (!(snmp_query_status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR)) { + int pcliberr = 0; + int psnmperr = 0; + char *pperrstring = NULL; + snmp_error(active_session, &pcliberr, &psnmperr, &pperrstring); + + if (psnmperr == SNMPERR_TIMEOUT) { + // We exit with critical here for some historical reason + die(STATE_CRITICAL, "SNMP query ran into a timeout\n"); + } + die(STATE_UNKNOWN, "SNMP query failed: %s\n", pperrstring); + } + + snmp_close(active_session); + + /* disable alarm again */ + alarm(0); + + snmp_responces result = { + .errorcode = OK, + .response_values = calloc(parameters.num_of_test_units, sizeof(response_value)), + }; + + if (result.response_values == NULL) { + result.errorcode = ERROR; + return result; + } + + // We got the the query results, now process them + size_t loop_index = 0; + for (netsnmp_variable_list *vars = response->variables; vars; + vars = vars->next_variable, loop_index++) { + + for (size_t jdx = 0; jdx < vars->name_length; jdx++) { + result.response_values[loop_index].oid[jdx] = vars->name[jdx]; + } + result.response_values[loop_index].oid_length = vars->name_length; + + switch (vars->type) { + case ASN_OCTET_STR: { + result.response_values[loop_index].string_response = strdup((char *)vars->val.string); + result.response_values[loop_index].type = vars->type; + if (verbose) { + printf("Debug: Got a string as response: %s\n", vars->val.string); + } + } + continue; + case ASN_OPAQUE: + if (verbose) { + printf("Debug: Got OPAQUE\n"); + } + break; + /* Numerical values */ + case ASN_COUNTER64: { + if (verbose) { + printf("Debug: Got counter64\n"); + } + struct counter64 tmp = *(vars->val.counter64); + uint64_t counter = (tmp.high << 32) + tmp.low; + result.response_values[loop_index].value.uIntVal = counter; + result.response_values[loop_index].type = vars->type; + } break; + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + if (verbose) { + printf("Debug: Got a Integer like\n"); + } + result.response_values[loop_index].value.uIntVal = (unsigned long)*(vars->val.integer); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_INTEGER: { + if (verbose) { + printf("Debug: Got a Integer\n"); + } + result.response_values[loop_index].value.intVal = *(vars->val.integer); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_FLOAT: { + if (verbose) { + printf("Debug: Got a float\n"); + } + result.response_values[loop_index].value.doubleVal = *(vars->val.floatVal); + result.response_values[loop_index].type = vars->type; + } break; + case ASN_DOUBLE: { + if (verbose) { + printf("Debug: Got a double\n"); + } + result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal); + result.response_values[loop_index].type = vars->type; + break; + } + case ASN_IPADDRESS: + if (verbose) { + printf("Debug: Got an IP address\n"); + } + result.response_values[loop_index].type = vars->type; + + // TODO: print address here, state always ok? or regex match? + continue; + default: + if (verbose) { + printf("Debug: Got a unmatched result type: %hhu\n", vars->type); + } + // TODO: Error here? + continue; + } + } + + return result; +} + +check_snmp_evaluation evaluate_single_unit(response_value response, + check_snmp_evaluation_parameters eval_params, + check_snmp_test_unit test_unit, time_t query_timestamp, + check_snmp_state_entry prev_state, + bool have_previous_state) { + mp_subcheck sc_oid_test = mp_subcheck_init(); + + if ((test_unit.label != NULL) && (strcmp(test_unit.label, "") != 0)) { + xasprintf(&sc_oid_test.output, "%s - ", test_unit.label); + } else { + sc_oid_test.output = strdup(""); + } + + char oid_string[(MAX_OID_LEN * 2) + 1] = {}; + + int oid_string_result = + snprint_objid(oid_string, (MAX_OID_LEN * 2) + 1, response.oid, response.oid_length); + if (oid_string_result <= 0) { + // TODO error here + die(STATE_UNKNOWN, "snprint_objid failed\n"); + } + + xasprintf(&sc_oid_test.output, "%sOID: %s", sc_oid_test.output, oid_string); + sc_oid_test = mp_set_subcheck_default_state(sc_oid_test, STATE_OK); + + if (verbose > 2) { + printf("Processing oid %s\n", oid_string); + } + + bool got_a_numerical_value = false; + mp_perfdata_value pd_result_val = {0}; + + check_snmp_state_entry result_state = { + .timestamp = query_timestamp, + .oid_length = response.oid_length, + .type = response.type, + }; + + for (size_t i = 0; i < response.oid_length; i++) { + result_state.oid[i] = response.oid[i]; + } + + if (have_previous_state) { + if (query_timestamp == prev_state.timestamp) { + // somehow we have the same timestamp again, that can't be good + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_UNKNOWN); + xasprintf(&sc_oid_test.output, "Time duration between plugin calls is invalid"); + + check_snmp_evaluation result = { + .sc = sc_oid_test, + .state = result_state, + }; + + return result; + } + } + // compute rate time difference + double timeDiff = 0; + if (have_previous_state) { + if (verbose) { + printf("Previous timestamp: %s", ctime(&prev_state.timestamp)); + printf("Current timestamp: %s", ctime(&query_timestamp)); + } + timeDiff = difftime(query_timestamp, prev_state.timestamp) / eval_params.rate_multiplier; + } + + mp_perfdata pd_num_val = {}; + + switch (response.type) { + case ASN_OCTET_STR: { + char *tmp = response.string_response; + if (strchr(tmp, '"') != NULL) { + // got double quote in the string + if (strchr(tmp, '\'') != NULL) { + // got single quote in the string too + // dont quote that at all to avoid even more confusion + xasprintf(&sc_oid_test.output, "%s - Value: %s", sc_oid_test.output, tmp); + } else { + // quote with single quotes + xasprintf(&sc_oid_test.output, "%s - Value: '%s'", sc_oid_test.output, tmp); + } + } else { + // quote with double quotes + xasprintf(&sc_oid_test.output, "%s - Value: \"%s\"", sc_oid_test.output, tmp); + } + + if (strlen(tmp) == 0) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, eval_params.nulloid_result); + } + + // String matching test + if ((test_unit.eval_mthd.crit_string)) { + if (strcmp(tmp, eval_params.string_cmp_value)) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_CRITICAL : STATE_OK); + } else { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL); + } + } else if (test_unit.eval_mthd.crit_regex) { + const size_t nmatch = eval_params.regex_cmp_value.re_nsub + 1; + regmatch_t pmatch[nmatch]; + memset(pmatch, '\0', sizeof(regmatch_t) * nmatch); + + int excode = regexec(&eval_params.regex_cmp_value, tmp, nmatch, pmatch, 0); + if (excode == 0) { + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, (eval_params.invert_search) ? STATE_OK : STATE_CRITICAL); + } else if (excode != REG_NOMATCH) { + char errbuf[MAX_INPUT_BUFFER] = ""; + regerror(excode, &eval_params.regex_cmp_value, errbuf, MAX_INPUT_BUFFER); + printf(_("Execute Error: %s\n"), errbuf); + exit(STATE_CRITICAL); + } else { // REG_NOMATCH + sc_oid_test = mp_set_subcheck_state( + sc_oid_test, eval_params.invert_search ? STATE_CRITICAL : STATE_OK); + } + } + } break; + case ASN_COUNTER64: + got_a_numerical_value = true; + + result_state.value.uIntVal = response.value.uIntVal; + result_state.type = response.type; + + // TODO: perfdata unit counter + if (eval_params.calculate_rate && have_previous_state) { + if (prev_state.value.uIntVal > response.value.uIntVal) { + // overflow + unsigned long long tmp = + (UINT64_MAX - prev_state.value.uIntVal) + response.value.uIntVal; + + tmp /= timeDiff; + pd_result_val = mp_create_pd_value(tmp); + } else { + pd_result_val = mp_create_pd_value( + (response.value.uIntVal - prev_state.value.uIntVal) / timeDiff); + } + } else { + // It's only a counter if we cont compute rate + pd_num_val.uom = "c"; + pd_result_val = mp_create_pd_value(response.value.uIntVal); + } + break; + case ASN_GAUGE: // same as ASN_UNSIGNED + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: { + got_a_numerical_value = true; + long long treated_value = (long long)response.value.uIntVal; + + if (eval_params.multiplier_set || eval_params.offset_set) { + double processed = 0; + if (eval_params.offset_set) { + processed += eval_params.offset; + } + + if (eval_params.multiplier_set) { + processed = processed * eval_params.multiplier; + } + + treated_value = lround(processed); + } + + result_state.value.intVal = treated_value; + + if (eval_params.calculate_rate && have_previous_state) { + if (verbose > 2) { + printf("%s: Rate calculation (int/counter/gauge): prev: %lli\n", __FUNCTION__, + prev_state.value.intVal); + printf("%s: Rate calculation (int/counter/gauge): current: %lli\n", __FUNCTION__, + treated_value); + } + double rate = (treated_value - prev_state.value.intVal) / timeDiff; + pd_result_val = mp_create_pd_value(rate); + } else { + pd_result_val = mp_create_pd_value(treated_value); + + if (response.type == ASN_COUNTER) { + pd_num_val.uom = "c"; + } + } + + } break; + case ASN_INTEGER: { + if (eval_params.multiplier_set || eval_params.offset_set) { + double processed = 0; + if (eval_params.multiplier_set) { + processed = (double)response.value.intVal * eval_params.multiplier; + } + + if (eval_params.offset_set) { + processed += eval_params.offset; + } + + result_state.value.doubleVal = processed; + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = + mp_create_pd_value((processed - prev_state.value.doubleVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(processed); + } + } else { + result_state.value.intVal = response.value.intVal; + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = mp_create_pd_value( + (response.value.intVal - prev_state.value.intVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(response.value.intVal); + } + } + + got_a_numerical_value = true; + } break; + case ASN_FLOAT: // fallthrough + case ASN_DOUBLE: { + got_a_numerical_value = true; + double tmp = response.value.doubleVal; + if (eval_params.offset_set) { + tmp += eval_params.offset; + } + + if (eval_params.multiplier_set) { + tmp *= eval_params.multiplier; + } + + if (eval_params.calculate_rate && have_previous_state) { + pd_result_val = mp_create_pd_value((tmp - prev_state.value.doubleVal) / timeDiff); + } else { + pd_result_val = mp_create_pd_value(tmp); + } + got_a_numerical_value = true; + + result_state.value.doubleVal = tmp; + } break; + case ASN_IPADDRESS: + // TODO + } + + if (got_a_numerical_value) { + if (eval_params.use_oid_as_perf_data_label) { + // Use oid for perdata label + pd_num_val.label = strdup(oid_string); + // TODO strdup error checking + } else if (test_unit.label != NULL && strcmp(test_unit.label, "") != 0) { + pd_num_val.label = strdup(test_unit.label); + } else { + pd_num_val.label = strdup(test_unit.oid); + } + + if (!(eval_params.calculate_rate && !have_previous_state)) { + // some kind of numerical value + if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { + pd_num_val.uom = test_unit.unit_value; + } + + pd_num_val.value = pd_result_val; + + xasprintf(&sc_oid_test.output, "%s Value: %s", sc_oid_test.output, + pd_value_to_string(pd_result_val)); + + if (test_unit.unit_value != NULL && strcmp(test_unit.unit_value, "") != 0) { + xasprintf(&sc_oid_test.output, "%s%s", sc_oid_test.output, test_unit.unit_value); + } + + if (test_unit.threshold.warning_is_set || test_unit.threshold.critical_is_set) { + pd_num_val = mp_pd_set_thresholds(pd_num_val, test_unit.threshold); + mp_state_enum tmp_state = mp_get_pd_status(pd_num_val); + + if (tmp_state == STATE_WARNING) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_WARNING); + xasprintf(&sc_oid_test.output, "%s - number violates warning threshold", + sc_oid_test.output); + } else if (tmp_state == STATE_CRITICAL) { + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_CRITICAL); + xasprintf(&sc_oid_test.output, "%s - number violates critical threshold", + sc_oid_test.output); + } + } + + mp_add_perfdata_to_subcheck(&sc_oid_test, pd_num_val); + } else { + // should calculate rate, but there is no previous state, so first run + // exit with ok now + sc_oid_test = mp_set_subcheck_state(sc_oid_test, STATE_OK); + xasprintf(&sc_oid_test.output, "%s - No previous data to calculate rate - assume okay", + sc_oid_test.output); + } + } + + check_snmp_evaluation result = { + .sc = sc_oid_test, + .state = result_state, + }; + + return result; +} + +char *_np_state_generate_key(int argc, char **argv); + +/* + * If time=NULL, use current time. Create state file, with state format + * version, default text. Writes version, time, and data. Avoid locking + * problems - use mv to write and then swap. Possible loss of state data if + * two things writing to same key at same time. + * Will die with UNKNOWN if errors + */ +void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore) { + time_t current_time; + if (timestamp == 0) { + time(¤t_time); + } else { + current_time = timestamp; + } + + int result = 0; + + /* If file doesn't currently exist, create directories */ + if (access(stateKey._filename, F_OK) != 0) { + char *directories = NULL; + result = asprintf(&directories, "%s", stateKey._filename); + if (result < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + for (char *p = directories + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if ((access(directories, F_OK) != 0) && (mkdir(directories, S_IRWXU) != 0)) { + /* Can't free this! Otherwise error message is wrong! */ + /* np_free(directories); */ + die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories); + } + *p = '/'; + } + } + + if (directories) { + free(directories); + } + } + + char *temp_file = NULL; + result = asprintf(&temp_file, "%s.XXXXXX", stateKey._filename); + if (result < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + int temp_file_desc = 0; + if ((temp_file_desc = mkstemp(temp_file)) == -1) { + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Cannot create temporary filename")); + } + + FILE *temp_file_pointer = fdopen(temp_file_desc, "w"); + if (temp_file_pointer == NULL) { + close(temp_file_desc); + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Unable to open temporary state file")); + } + + fprintf(temp_file_pointer, "# NP State file\n"); + fprintf(temp_file_pointer, "%d\n", NP_STATE_FORMAT_VERSION); + fprintf(temp_file_pointer, "%d\n", stateKey.data_version); + fprintf(temp_file_pointer, "%lu\n", current_time); + fprintf(temp_file_pointer, "%s\n", stringToStore); + + fchmod(temp_file_desc, S_IRUSR | S_IWUSR | S_IRGRP); + + fflush(temp_file_pointer); + + result = fclose(temp_file_pointer); + + fsync(temp_file_desc); + + if (result != 0) { + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Error writing temp file")); + } + + if (rename(temp_file, stateKey._filename) != 0) { + unlink(temp_file); + if (temp_file) { + free(temp_file); + } + die(STATE_UNKNOWN, _("Cannot rename state temp file")); + } + + if (temp_file) { + free(temp_file); + } +} + +/* + * Read the state file + */ +bool _np_state_read_file(FILE *state_file, state_key stateKey) { + time_t current_time; + time(¤t_time); + + /* Note: This introduces a limit of 8192 bytes in the string data */ + char *line = (char *)calloc(1, 8192); + if (line == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + bool status = false; + enum { + STATE_FILE_VERSION, + STATE_DATA_VERSION, + STATE_DATA_TIME, + STATE_DATA_TEXT, + STATE_DATA_END + } expected = STATE_FILE_VERSION; + + int failure = 0; + while (!failure && (fgets(line, 8192, state_file)) != NULL) { + size_t pos = strlen(line); + if (line[pos - 1] == '\n') { + line[pos - 1] = '\0'; + } + + if (line[0] == '#') { + continue; + } + + switch (expected) { + case STATE_FILE_VERSION: { + int i = atoi(line); + if (i != NP_STATE_FORMAT_VERSION) { + failure++; + } else { + expected = STATE_DATA_VERSION; + } + } break; + case STATE_DATA_VERSION: { + int i = atoi(line); + if (i != stateKey.data_version) { + failure++; + } else { + expected = STATE_DATA_TIME; + } + } break; + case STATE_DATA_TIME: { + /* If time > now, error */ + time_t data_time = strtoul(line, NULL, 10); + if (data_time > current_time) { + failure++; + } else { + stateKey.state_data->time = data_time; + expected = STATE_DATA_TEXT; + } + } break; + case STATE_DATA_TEXT: + stateKey.state_data->data = strdup(line); + if (stateKey.state_data->data == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + stateKey.state_data->length = strlen(line); + expected = STATE_DATA_END; + status = true; + break; + case STATE_DATA_END:; + } + } + + if (line) { + free(line); + } + return status; +} +/* + * Will return NULL if no data is available (first run). If key currently + * exists, read data. If state file format version is not expected, return + * as if no data. Get state data version number and compares to expected. + * If numerically lower, then return as no previous state. die with UNKNOWN + * if exceptional error. + */ +state_data *np_state_read(state_key stateKey) { + /* Open file. If this fails, no previous state found */ + FILE *statefile = fopen(stateKey._filename, "r"); + state_data *this_state_data = (state_data *)calloc(1, sizeof(state_data)); + if (statefile != NULL) { + + if (this_state_data == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + this_state_data->data = NULL; + stateKey.state_data = this_state_data; + + if (_np_state_read_file(statefile, stateKey)) { + this_state_data->errorcode = OK; + } else { + this_state_data->errorcode = ERROR; + } + + fclose(statefile); + } else { + // Failed to open state file + this_state_data->errorcode = ERROR; + } + + return stateKey.state_data; +} + +/* + * Internal function. Returns either: + * envvar NAGIOS_PLUGIN_STATE_DIRECTORY + * statically compiled shared state directory + */ +char *_np_state_calculate_location_prefix(void) { + char *env_dir; + + /* Do not allow passing MP_STATE_PATH in setuid plugins + * for security reasons */ + if (!mp_suid()) { + env_dir = getenv("MP_STATE_PATH"); + if (env_dir && env_dir[0] != '\0') { + return env_dir; + } + /* This is the former ENV, for backward-compatibility */ + env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY"); + if (env_dir && env_dir[0] != '\0') { + return env_dir; + } + } + + return NP_STATE_DIR_PREFIX; +} + +/* + * Initiatializer for state routines. + * Sets variables. Generates filename. Returns np_state_key. die with + * UNKNOWN if exception + */ +state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, + char **argv) { + state_key *this_state = (state_key *)calloc(1, sizeof(state_key)); + if (this_state == NULL) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + char *temp_keyname = NULL; + if (keyname == NULL) { + temp_keyname = _np_state_generate_key(argc, argv); + } else { + temp_keyname = strdup(keyname); + if (temp_keyname == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + } + + /* Die if invalid characters used for keyname */ + char *tmp_char = temp_keyname; + while (*tmp_char != '\0') { + if (!(isalnum(*tmp_char) || *tmp_char == '_')) { + die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'")); + } + tmp_char++; + } + this_state->name = temp_keyname; + this_state->plugin_name = plugin_name; + this_state->data_version = expected_data_version; + this_state->state_data = NULL; + + /* Calculate filename */ + char *temp_filename = NULL; + int error = asprintf(&temp_filename, "%s/%lu/%s/%s", _np_state_calculate_location_prefix(), + (unsigned long)geteuid(), plugin_name, this_state->name); + if (error < 0) { + die(STATE_UNKNOWN, _("Cannot allocate memory: %s"), strerror(errno)); + } + + this_state->_filename = temp_filename; + + return *this_state; +} + +/* + * Returns a string to use as a keyname, based on an md5 hash of argv, thus + * hopefully a unique key per service/plugin invocation. Use the extra-opts + * parse of argv, so that uniqueness in parameters are reflected there. + */ +char *_np_state_generate_key(int argc, char **argv) { + unsigned char result[256]; + +#ifdef USE_OPENSSL + /* + * This code path is chosen if openssl is available (which should be the most common + * scenario). Alternatively, the gnulib implementation/ + * + */ + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + + EVP_DigestInit(ctx, EVP_sha256()); + + for (int i = 0; i < argc; i++) { + EVP_DigestUpdate(ctx, argv[i], strlen(argv[i])); + } + + EVP_DigestFinal(ctx, result, NULL); +#else + + struct sha256_ctx ctx; + + for (int i = 0; i < this_monitoring_plugin->argc; i++) { + sha256_process_bytes(argv[i], strlen(argv[i]), &ctx); + } + + sha256_finish_ctx(&ctx, result); +#endif // FOUNDOPENSSL + + char keyname[41]; + for (int i = 0; i < 20; ++i) { + sprintf(&keyname[2 * i], "%02x", result[i]); + } + + keyname[40] = '\0'; + + char *keyname_copy = strdup(keyname); + if (keyname_copy == NULL) { + die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno)); + } + + return keyname_copy; +} diff --git a/plugins/check_snmp.d/check_snmp_helpers.h b/plugins/check_snmp.d/check_snmp_helpers.h index 28e3c4e3..0f7780b1 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.h +++ b/plugins/check_snmp.d/check_snmp_helpers.h @@ -1,7 +1,71 @@ #pragma once #include "./config.h" +#include check_snmp_test_unit check_snmp_test_unit_init(); int check_snmp_set_thresholds(const char *, check_snmp_test_unit[], size_t, bool); check_snmp_config check_snmp_config_init(); + +typedef struct { + oid oid[MAX_OID_LEN]; + size_t oid_length; + unsigned char type; + union { + uint64_t uIntVal; + int64_t intVal; + double doubleVal; + } value; + char *string_response; +} response_value; + +typedef struct { + int errorcode; + response_value *response_values; +} snmp_responces; +snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters); + +// state is similar to response, but only numerics and a timestamp +typedef struct { + time_t timestamp; + oid oid[MAX_OID_LEN]; + size_t oid_length; + unsigned char type; + union { + unsigned long long uIntVal; + long long intVal; + double doubleVal; + } value; +} check_snmp_state_entry; + +typedef struct { + check_snmp_state_entry state; + mp_subcheck sc; +} check_snmp_evaluation; +check_snmp_evaluation evaluate_single_unit(response_value response, + check_snmp_evaluation_parameters eval_params, + check_snmp_test_unit test_unit, time_t query_timestamp, + check_snmp_state_entry prev_state, + bool have_previous_state); + +#define NP_STATE_FORMAT_VERSION 1 + +typedef struct state_data_struct { + time_t time; + void *data; + size_t length; /* Of binary data */ + int errorcode; +} state_data; + +typedef struct state_key_struct { + char *name; + char *plugin_name; + int data_version; + char *_filename; + state_data *state_data; +} state_key; + +state_data *np_state_read(state_key stateKey); +state_key np_enable_state(char *keyname, int expected_data_version, char *plugin_name, int argc, + char **argv); +void np_state_write_string(state_key stateKey, time_t timestamp, char *stringToStore); diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index e68986e2..a5d5aa52 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -1,7 +1,7 @@ #pragma once -#include "thresholds.h" -#include "states.h" +#include "../../lib/thresholds.h" +#include "../../lib/states.h" #include #include #include @@ -18,7 +18,7 @@ #include #include -#define DEFAULT_PORT "161" +#define DEFAULT_PORT "161" #define DEFAULT_RETRIES 5 typedef struct eval_method { @@ -34,10 +34,8 @@ typedef struct check_snmp_test_unit { mp_thresholds threshold; } check_snmp_test_unit; -typedef struct check_snmp_config { - // SNMP session to use +typedef struct { struct snmp_session snmp_session; - // use getnet instead of get bool use_getnext; @@ -47,7 +45,9 @@ typedef struct check_snmp_config { check_snmp_test_unit *test_units; size_t num_of_test_units; +} check_snmp_config_snmp_parameters; +typedef struct { // State if an empty value is encountered mp_state_enum nulloid_result; @@ -63,7 +63,18 @@ typedef struct check_snmp_config { bool offset_set; // Modify output - bool use_perf_data_labels_from_input; + bool use_oid_as_perf_data_label; + + // activate rate calucation + bool calculate_rate; + unsigned int rate_multiplier; +} check_snmp_evaluation_parameters; + +typedef struct check_snmp_config { + // SNMP session to use + check_snmp_config_snmp_parameters snmp_params; + + check_snmp_evaluation_parameters evaluation_params; mp_output_format output_format; bool output_format_is_set; diff --git a/plugins/tests/check_snmp.t b/plugins/tests/check_snmp.t index bfe42e16..26d67898 100755 --- a/plugins/tests/check_snmp.t +++ b/plugins/tests/check_snmp.t @@ -4,12 +4,13 @@ # use strict; +use warnings; use Test::More; use NPTest; use FindBin qw($Bin); use POSIX qw/strftime/; -my $tests = 81; +my $tests = 75; # Check that all dependent modules are available eval { require NetSNMP::OID; @@ -76,49 +77,36 @@ my $res; $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying a multi-line string" ); -like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); -like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software | -.1.3.6.1.4.1.8072.3.2.67.0: -"Cisco Internetwork Operating System Software -IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version -12.2(20)EWA, RELEASE SOFTWARE (fc1) -Technical Support: http://www.cisco.com/techsupport -Copyright (c) 1986-2004 by cisco Systems, Inc. -"').'/m', "String contains all lines"); +like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines"); # sysContact.0 is "Alice" (from our snmpd.conf) $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.0 -o sysContact.0 -o .1.3.6.1.4.1.8072.3.2.67.1"); cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); -like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); -like($res->output, '/'.quotemeta('SNMP OK - Cisco Internetwork Operating System Software ').'"?Alice"?'.quotemeta(' Kisco Outernetwork Oserating Gystem Totware | -.1.3.6.1.4.1.8072.3.2.67.0: -"Cisco Internetwork Operating System Software -IOS (tm) Catalyst 4000 \"L3\" Switch Software (cat4000-I9K91S-M), Version -12.2(20)EWA, RELEASE SOFTWARE (fc1) -Technical Support: http://www.cisco.com/techsupport -Copyright (c) 1986-2004 by cisco Systems, Inc. -" -.1.3.6.1.4.1.8072.3.2.67.1: -"Kisco Outernetwork Oserating Gystem Totware -Copyleft (c) 2400-2689 by kisco Systrems, Inc."').'/m', "String contains all lines with multiple OIDs"); +# like($res->output, '/^SNMP OK - /', "String contains SNMP OK"); +like($res->output, '/.*Cisco Internetwork Operating System Software.*/m', "String contains all lines with multiple OIDs"); +like($res->output, '/.*Alice.*/m', "String contains all lines with multiple OIDs"); +like($res->output, '/.*Kisco Outernetwork Oserating Gystem Totware.*/m', "String contains all lines with multiple OIDs"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.2"); -like($res->output, '/'.quotemeta('SNMP OK - This should not confuse check_snmp \"parser\" | -.1.3.6.1.4.1.8072.3.2.67.2: -"This should not confuse check_snmp \"parser\" -into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('This should not confuse check_snmp \"parser\" | +# .1.3.6.1.4.1.8072.3.2.67.2: +# "This should not confuse check_snmp \"parser\" +# into thinking there is no 2nd line"').'/m', "Attempt to confuse parser No.1"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.3"); -like($res->output, '/'.quotemeta('SNMP OK - It\'s getting even harder if the line | -.1.3.6.1.4.1.8072.3.2.67.3: -"It\'s getting even harder if the line -ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('It\'s getting even harder if the line | +# .1.3.6.1.4.1.8072.3.2.67.3: +# "It\'s getting even harder if the line +# ends with with this: C:\\\\"').'/m', "Attempt to confuse parser No.2"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.4"); -like($res->output, '/'.quotemeta('SNMP OK - And now have fun with with this: \"C:\\\\\" | -.1.3.6.1.4.1.8072.3.2.67.4: -"And now have fun with with this: \"C:\\\\\" -because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); +cmp_ok( $res->return_code, '==', 0, "Exit OK when querying multi-line OIDs" ); +# like($res->output, '/'.quotemeta('And now have fun with with this: \"C:\\\\\" | +# .1.3.6.1.4.1.8072.3.2.67.4: +# "And now have fun with with this: \"C:\\\\\" +# because we\'re not done yet!"').'/m', "Attempt to confuse parser No.3"); system("rm -f ".$ENV{'MP_STATE_PATH'}."/*/check_snmp/*"); @@ -131,156 +119,159 @@ SKIP: { my $ts = time(); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 0, "Returns OK"); - is($res->output, "No previous data to calculate rate - assume okay"); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/"); # test rate 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 1, "WARNING - due to going above rate calculation" ); - is($res->output, "SNMP RATE WARNING - *666* | iso.3.6.1.4.1.8072.3.2.67.10=666;600 "); + like($res->output, "/.*=666.*/"); # test rate with same time $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -w 600" ); is($res->return_code, 3, "UNKNOWN - basically the divide by zero error" ); - is($res->output, "Time duration between plugin calls is invalid"); + like($res->output, "/.*Time duration between plugin calls is invalid.*/"); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK for first call" ); - is($res->output, "No previous data to calculate rate - assume okay" ); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/" ); # test rate 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets 666 | inoctets=666 ", "Check label"); + like($res->output, "/.*inoctets.*=666.*/m", "Check label"); # test rate 3 seconds later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+3))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets 333 | inoctets=333 ", "Check rate decreases due to longer interval"); + like($res->output, "/.*inoctets.*333.*/", "Check rate decreases due to longer interval"); # label performance data check $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test 67996 | test=67996c ", "Check label"); + like($res->output, "/.*test.?=67996c/", "Check label"); - $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); - is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test'test 68662 | \"test'test\"=68662c ", "Check label"); + # following test is deactivated since it was not valid due to the guidelines (no single quote in label allowed) + # $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l \"test'test\"" ); + # is($res->return_code, 0, "OK as no thresholds" ); + # is($res->output, "test'test 68662 | \"test'test\"=68662c ", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test\"test'" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test\"test 69328 | 'test\"test'=69328c ", "Check label"); + like($res->output, "/.*'test\"test'=68662c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l test -O" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test 69994 | iso.3.6.1.4.1.8072.3.2.67.10=69994c ", "Check label"); + like($res->output, "/.*.67.10.?=69328c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - 70660 | iso.3.6.1.4.1.8072.3.2.67.10=70660c ", "Check label"); + like($res->output, "/.*8072.3.2.67.10.?=69994c.*/", "Check label"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 -l 'test test'" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP OK - test test 71326 | 'test test'=71326c ", "Check label"); + like($res->output, "/.*'test test'=70660c/", "Check label"); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); is($res->return_code, 0, "OK for first call" ); - is($res->output, "No previous data to calculate rate - assume okay" ); + like($res->output, "/.*No previous data to calculate rate - assume okay.*/" ); # test 1 second later $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10 --rate -l inoctets_per_minute --rate-multiplier=60" ); is($res->return_code, 0, "OK as no thresholds" ); - is($res->output, "SNMP RATE OK - inoctets_per_minute 39960 | inoctets_per_minute=39960 ", "Checking multiplier"); + like($res->output, "/.*inoctets_per_minute.*=39960/", "Checking multiplier"); }; -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s '\"stringtests\"'" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s 'stringtests'" ); is($res->return_code, 0, "OK as string matches" ); -is($res->output, 'SNMP OK - "stringtests" | ', "Good string match" ); +like($res->output, '/.*stringtests.*/', "Good string match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 -s ring" ); is($res->return_code, 2, "CRITICAL as string doesn't match (though is a substring)" ); -is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Failed string match" ); +like($res->output, '/.*stringtests.*/', "Failed string match" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s '\"stringtests\"'" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s 'stringtests'" ); is($res->return_code, 2, "CRITICAL as string matches but inverted" ); -is($res->output, 'SNMP CRITICAL - *"stringtests"* | ', "Inverted string match" ); +like($res->output, '/.*"stringtests".*/', "Inverted string match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.11 --invert-search -s ring" ); is($res->return_code, 0, "OK as string doesn't match but inverted" ); -is($res->output, 'SNMP OK - "stringtests" | ', "OK as inverted string no match" ); +like($res->output, '/.*"stringtests".*/', "OK as inverted string no match" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.12 -w 4:5" ); -is($res->return_code, 1, "Numeric in string test" ); -is($res->output, 'SNMP WARNING - *3.5* | iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5 ', "WARNING threshold checks for string masquerading as number" ); +# a string is a string and not a number +is($res->return_code, 0, "Numeric in string test" ); +like($res->output, '/.*3.5.*| iso.3.6.1.4.1.8072.3.2.67.12=3.5;4:5/', "WARNING threshold checks for string masquerading as number" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.13" ); is($res->return_code, 0, "Not really numeric test" ); -is($res->output, 'SNMP OK - "87.4startswithnumberbutshouldbestring" | ', "Check string with numeric start is still string" ); +like($res->output, '/.*"87.4startswithnumberbutshouldbestring".*/', "Check string with numeric start is still string" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.14" ); is($res->return_code, 0, "Not really numeric test (trying best to fool it)" ); -is($res->output, 'SNMP OK - "555\"I said\"" | ', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); +like($res->output, '/.*\'555"I said"\'.*/', "Check string with a double quote following is still a string (looks like the perl routine will always escape though)" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.15 -r 'CUSTOM CHECK OK'" ); is($res->return_code, 0, "String check should check whole string, not a parsed number" ); -is($res->output, 'SNMP OK - "CUSTOM CHECK OK: foo is 12345" | ', "String check with numbers returns whole string"); +like($res->output, '/.*CUSTOM CHECK OK: foo is 12345.*/', "String check with numbers returns whole string"); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 0, "Negative integer check OK" ); -is($res->output, 'SNMP OK - -2 | iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3: ', "Negative integer check OK output" ); +like($res->output, '/.*-2.*| iso.3.6.1.4.1.8072.3.2.67.16=-2;-2:;-3:/', "Negative integer check OK output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 1, "Negative integer check WARNING" ); -is($res->output, 'SNMP WARNING - *-3* | iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3: ', "Negative integer check WARNING output" ); +like($res->output, '/.*-3.*| iso.3.6.1.4.1.8072.3.2.67.16=-3;-2:;-3:/', "Negative integer check WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.16 -w -2: -c -3:" ); is($res->return_code, 2, "Negative integer check CRITICAL" ); -is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3: ', "Negative integer check CRITICAL output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.16=-4;-2:;-3:/', "Negative integer check CRITICAL output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -3: -c -6:" ); is($res->return_code, 1, "Negative integer as string, WARNING" ); -is($res->output, 'SNMP WARNING - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6: ', "Negative integer as string, WARNING output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-3:;-6:/', "Negative integer as string, WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.17 -w -2: -c -3:" ); is($res->return_code, 2, "Negative integer as string, CRITICAL" ); -is($res->output, 'SNMP CRITICAL - *-4* | iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3: ', "Negative integer as string, CRITICAL output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.17=-4;-2:;-3:/', "Negative integer as string, CRITICAL output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); -is($res->return_code, 0, "Negative float OK" ); -is($res->output, 'SNMP OK - -6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); +# deactivated since the perl agent api of snmpd really does not allow floats +# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -c '~:-6.5'" ); +# is($res->return_code, 0, "Negative float OK" ); +# is($res->output, '-6.6 | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;;@-6.5:~ ', "Negative float OK output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); -is($res->return_code, 1, "Negative float WARNING" ); -is($res->output, 'SNMP WARNING - *-6.6* | iso.3.6.1.4.1.8072.3.2.67.18=-6.6;@-6.65:~;@-6.55:~ ', "Negative float WARNING output" ); +# $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.18 -w '~:-6.65' -c '~:-6.55'" ); +# is($res->return_code, 1, "Negative float WARNING" ); +# like($res->output, '/-6.6.*| .*67.18=-6.6;@-6.65:~;@-6.55:~/', "Negative float WARNING output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-10:20' -c '2:200000,-20:30'" ); is($res->return_code, 0, "Multiple OIDs with thresholds" ); -like($res->output, '/SNMP OK - \d+ -4 | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); +like($res->output, '/-4.*| .*67.10=\d+c;1:100000;2:200000 .*67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w '1:100000,-1:2' -c '2:200000,-20:30'" ); is($res->return_code, 1, "Multiple OIDs with thresholds" ); -like($res->output, '/SNMP WARNING - \d+ \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); +like($res->output, '/-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1:100000;2:200000 iso.3.6.1.4.1.8072.3.2.67.17=-4;-10:20;-20:30/', "Multiple OIDs with thresholds output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.10,.1.3.6.1.4.1.8072.3.2.67.17 -w 1,2 -c 1 -O" ); is($res->return_code, 2, "Multiple OIDs with some thresholds" ); -like($res->output, '/SNMP CRITICAL - \*\d+\* \*-4\* | iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); +like($res->output, '/.*-4.*| iso.3.6.1.4.1.8072.3.2.67.10=\d+c;1;2 iso.3.6.1.4.1.8072.3.2.67.17=-4;;/', "Multiple OIDs with thresholds output" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19"); is($res->return_code, 0, "Test plain .1.3.6.1.4.1.8072.3.2.67.6 RC" ); -is($res->output,'SNMP OK - 42 | iso.3.6.1.4.1.8072.3.2.67.19=42 ', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); +like($res->output,'/.*42.*| iso.3.6.1.4.1.8072.3.2.67.19=42/', "Test plain value of .1.3.6.1.4.1.8072.3.2.67.1" ); $res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 -M .1"); is($res->return_code, 0, "Test multiply RC" ); -is($res->output,'SNMP OK - 4.200000 | iso.3.6.1.4.1.8072.3.2.67.19=4.200000 ' , "Test multiply .1 output" ); +like($res->output,'/.*4.200000.*| iso.3.6.1.4.1.8072.3.2.67.19=4.200000/' , "Test multiply .1 output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' "); -is($res->return_code, 0, "Test multiply RC + format" ); -is($res->output, 'SNMP OK - 4.20 | iso.3.6.1.4.1.8072.3.2.67.19=4.20 ', "Test multiply .1 output + format" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1"); +is($res->return_code, 0, "Test multiply RC" ); +like($res->output, '/.*4.20.*| iso.3.6.1.4.1.8072.3.2.67.19=4.20/', "Test multiply .1 output" ); -$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -f '%.2f' -w 1"); -is($res->return_code, 1, "Test multiply RC + format + thresholds" ); -is($res->output, 'SNMP WARNING - *4.20* | iso.3.6.1.4.1.8072.3.2.67.19=4.20;1 ', "Test multiply .1 output + format + thresholds" ); +$res = NPTest->testCmd( "./check_snmp -H 127.0.0.1 -C public -p $port_snmp -o .1.3.6.1.4.1.8072.3.2.67.19 --multiplier=.1 -w 1"); +is($res->return_code, 1, "Test multiply RC + thresholds" ); +like($res->output, '/.*4.20.* | iso.3.6.1.4.1.8072.3.2.67.19=4.20+;1/', "Test multiply .1 output + thresholds" ); diff --git a/plugins/tests/check_snmp_agent.pl b/plugins/tests/check_snmp_agent.pl index 38912e98..608b6f92 100644 --- a/plugins/tests/check_snmp_agent.pl +++ b/plugins/tests/check_snmp_agent.pl @@ -4,9 +4,10 @@ # #use strict; # Doesn't work +use warnings; use NetSNMP::OID qw(:all); use NetSNMP::agent; -use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64); +use NetSNMP::ASN qw(ASN_OCTET_STR ASN_COUNTER ASN_COUNTER64 ASN_INTEGER ASN_INTEGER64 ASN_UNSIGNED ASN_UNSIGNED64 ASN_FLOAT); #use Math::Int64 qw(uint64); # Skip that module while we don't need it sub uint64 { return $_ } @@ -22,21 +23,82 @@ IOS (tm) Catalyst 4000 "L3" Switch Software (cat4000-I9K91S-M), Version Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2004 by cisco Systems, Inc. '; -my $multilin2 = "Kisco Outernetwork Oserating Gystem Totware +my $multiline2 = "Kisco Outernetwork Oserating Gystem Totware Copyleft (c) 2400-2689 by kisco Systrems, Inc."; -my $multilin3 = 'This should not confuse check_snmp "parser" +my $multiline3 = 'This should not confuse check_snmp "parser" into thinking there is no 2nd line'; -my $multilin4 = 'It\'s getting even harder if the line +my $multiline4 = 'It\'s getting even harder if the line ends with with this: C:\\'; -my $multilin5 = 'And now have fun with with this: "C:\\" +my $multiline5 = 'And now have fun with with this: "C:\\" because we\'re not done yet!'; # Next are arrays of indexes (Type, initial value and increments) # 0..19 <---- please update comment when adding/removing fields -my @fields = (ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_UNSIGNED, ASN_UNSIGNED, ASN_COUNTER, ASN_COUNTER64, ASN_UNSIGNED, ASN_COUNTER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER, ASN_OCTET_STR, ASN_OCTET_STR, ASN_INTEGER ); -my @values = ($multiline, $multilin2, $multilin3, $multilin4, $multilin5, 4294965296, 1000, 4294965296, uint64("18446744073709351616"), int(rand(2**32)), 64000, "stringtests", "3.5", "87.4startswithnumberbutshouldbestring", '555"I said"', 'CUSTOM CHECK OK: foo is 12345', -2, '-4', '-6.6', 42 ); +my @fields = (ASN_OCTET_STR, # 0 + ASN_OCTET_STR, # 1 + ASN_OCTET_STR, # 2 + ASN_OCTET_STR, # 3 + ASN_OCTET_STR, # 4 + ASN_UNSIGNED, # 5 + ASN_UNSIGNED, # 6 + ASN_COUNTER, # 7 + ASN_COUNTER64, # 8 + ASN_UNSIGNED, # 9 + ASN_COUNTER, # 10 + ASN_OCTET_STR, # 11 + ASN_OCTET_STR, # 12 + ASN_OCTET_STR, # 13 + ASN_OCTET_STR, # 14 + ASN_OCTET_STR, # 15 + ASN_INTEGER, # 16 + ASN_INTEGER, # 17 + ASN_FLOAT, # 18 + ASN_INTEGER # 19 + ); +my @values = ($multiline, # 0 + $multiline2, # 1 + $multiline3, # 2 + $multiline4, # 3 + $multiline5, # 4 + 4294965296, # 5 + 1000, # 6 + 4294965296, # 7 + uint64("18446744073709351616"), # 8 + int(rand(2**32)), # 9 + 64000, # 10 + "stringtests", # 11 + "3.5", # 12 + "87.4startswithnumberbutshouldbestring", # 13 + '555"I said"', # 14 + 'CUSTOM CHECK OK: foo is 12345', # 15 + '-2', # 16 + '-4', # 17 + '-6.6', # 18 + 42 # 19 + ); # undef increments are randomized -my @incrts = (undef, undef, undef, undef, undef, 1000, -500, 1000, 100000, undef, 666, undef, undef, undef, undef, undef, -1, undef, undef, 0 ); +my @incrts = ( + undef, # 0 + undef, # 1 + undef, # 2 + undef, # 3 + undef, # 4 + 1000, # 5 + -500, # 6 + 1000, # 7 + 100000, # 8 + undef, # 9 + 666, # 10 + undef, # 11 + undef, # 12 + undef, # 13 + undef, # 14 + undef, # 15 + -1, # 16 + 0, # 17 + undef, # 18 + 0 # 19 + ); # Number of elements in our OID my $oidelts; diff --git a/plugins/tests/test_check_snmp.c b/plugins/tests/test_check_snmp.c new file mode 100644 index 00000000..d71706d0 --- /dev/null +++ b/plugins/tests/test_check_snmp.c @@ -0,0 +1,175 @@ +/***************************************************************************** + * + * 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 "tap.h" +#include "../../config.h" + +#include +#include +#include + +#include "utils_base.c" +#include "../check_snmp.d/check_snmp_helpers.h" + +char *_np_state_generate_key(int argc, char **argv); +char *_np_state_calculate_location_prefix(void); + +int main(int argc, char **argv) { + char *temp_string = (char *)_np_state_generate_key(argc, argv); + ok(!strcmp(temp_string, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), + "Got hash with exe and no parameters") || + diag("You are probably running in wrong directory. Must run as ./test_utils"); + + int fake_argc = 4; + char *fake_argv[] = { + "./test_utils", + "here", + "--and", + "now", + }; + temp_string = (char *)_np_state_generate_key(fake_argc, fake_argv); + ok(!strcmp(temp_string, "bd72da9f78ff1419fad921ea5e43ce56508aef6c"), + "Got based on expected argv"); + + unsetenv("MP_STATE_PATH"); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory"); + + setenv("MP_STATE_PATH", "", 1); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, NP_STATE_DIR_PREFIX), "Got default directory even with empty string"); + + setenv("MP_STATE_PATH", "/usr/local/nagios/var", 1); + temp_string = (char *)_np_state_calculate_location_prefix(); + ok(!strcmp(temp_string, "/usr/local/nagios/var"), "Got default directory"); + + fake_argc = 1; + fake_argv[0] = "./test_utils"; + state_key temp_state_key1 = np_enable_state(NULL, 51, "check_test", fake_argc, fake_argv); + ok(!strcmp(temp_state_key1.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key1.name, "e2d17f995fd4c020411b85e3e3d0ff7306d4147e"), + "Got generated filename"); + + state_key temp_state_key2 = + np_enable_state("allowedchars_in_keyname", 77, "check_snmp", fake_argc, fake_argv); + + char state_path[1024]; + sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/allowedchars_in_keyname", + (unsigned long)geteuid()); + ok(!strcmp(temp_state_key2.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key2.name, "allowedchars_in_keyname"), "Got key name with valid chars"); + ok(!strcmp(temp_state_key2._filename, state_path), "Got internal filename"); + + /* Don't do this test just yet. Will die */ + /* + np_enable_state("bad^chars$in@here", 77); + temp_state_key = this_monitoring_plugin->state; + ok( !strcmp(temp_state_key->name, "bad_chars_in_here"), "Got key name with bad chars replaced" + ); + */ + + state_key temp_state_key3 = + np_enable_state("funnykeyname", 54, "check_snmp", fake_argc, fake_argv); + sprintf(state_path, "/usr/local/nagios/var/%lu/check_test/funnykeyname", + (unsigned long)geteuid()); + ok(!strcmp(temp_state_key3.plugin_name, "check_test"), "Got plugin name"); + ok(!strcmp(temp_state_key3.name, "funnykeyname"), "Got key name"); + + ok(!strcmp(temp_state_key3._filename, state_path), "Got internal filename"); + ok(temp_state_key3.data_version == 54, "Version set"); + + state_data *temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Got no state data as file does not exist"); + + /* + temp_fp = fopen("var/statefile", "r"); + if (temp_fp==NULL) + printf("Error opening. errno=%d\n", errno); + printf("temp_fp=%s\n", temp_fp); + ok( _np_state_read_file(temp_fp) == true, "Can read state file" ); + fclose(temp_fp); + */ + + temp_state_key3._filename = "var/statefile"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data != NULL, "Got state data now") || + diag("Are you running in right directory? Will get coredump next if not"); + ok(temp_state_data->time == 1234567890, "Got time"); + ok(!strcmp((char *)temp_state_data->data, "String to read"), "Data as expected"); + + temp_state_key3.data_version = 53; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Older data version gives NULL"); + temp_state_key3.data_version = 54; + + temp_state_key3._filename = "var/nonexistent"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Missing file gives NULL"); + + temp_state_key3._filename = "var/oldformat"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Old file format gives NULL"); + + temp_state_key3._filename = "var/baddate"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Bad date gives NULL"); + + temp_state_key3._filename = "var/missingdataline"; + temp_state_data = np_state_read(temp_state_key3); + ok(temp_state_data == NULL, "Missing data line gives NULL"); + + unlink("var/generated"); + temp_state_key3._filename = "var/generated"; + + time_t current_time = 1234567890; + np_state_write_string(temp_state_key3, current_time, "String to read"); + ok(system("cmp var/generated var/statefile") == 0, "Generated file same as expected"); + + unlink("var/generated_directory/statefile"); + unlink("var/generated_directory"); + temp_state_key3._filename = "var/generated_directory/statefile"; + current_time = 1234567890; + np_state_write_string(temp_state_key3, current_time, "String to read"); + ok(system("cmp var/generated_directory/statefile var/statefile") == 0, + "Have created directory"); + + /* This test to check cannot write to dir - can't automate yet */ + /* + unlink("var/generated_bad_dir"); + mkdir("var/generated_bad_dir", S_IRUSR); + np_state_write_string(current_time, "String to read"); + */ + + temp_state_key3._filename = "var/generated"; + time(¤t_time); + np_state_write_string(temp_state_key3, 0, "String to read"); + temp_state_data = np_state_read(temp_state_key3); + /* Check time is set to current_time */ + ok(system("cmp var/generated var/statefile > /dev/null") != 0, + "Generated file should be different this time"); + ok(temp_state_data->time - current_time <= 1, "Has time generated from current time"); + + /* Don't know how to automatically test this. Need to be able to redefine die and catch the + * error */ + /* + temp_state_key->_filename="/dev/do/not/expect/to/be/able/to/write"; + np_state_write_string(0, "Bad file"); + */ + + np_cleanup(); +} diff --git a/plugins/tests/test_check_snmp.t b/plugins/tests/test_check_snmp.t new file mode 100755 index 00000000..967633e9 --- /dev/null +++ b/plugins/tests/test_check_snmp.t @@ -0,0 +1,6 @@ +#!/usr/bin/perl +use Test::More; +if (! -e "./test_check_snmp") { + plan skip_all => "./test_check_snmp not compiled - please enable libtap library to test"; +} +exec "./test_check_snmp"; -- cgit v1.2.3-74-g34f1 From be9db2e02f5e3ffdf14c84beb5382336bbfca063 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:59:20 +0200 Subject: lib: code formatting, perfdata label sanity checking and so on --- lib/perfdata.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/lib/perfdata.c b/lib/perfdata.c index b87de7e0..2930a8bc 100644 --- a/lib/perfdata.c +++ b/lib/perfdata.c @@ -33,7 +33,18 @@ char *pd_value_to_string(const mp_perfdata_value pd) { char *pd_to_string(mp_perfdata pd) { assert(pd.label != NULL); char *result = NULL; - asprintf(&result, "'%s'=", pd.label); + + if (strchr(pd.label, '\'') == NULL) { + asprintf(&result, "'%s'=", pd.label); + } else { + // we have a illegal single quote in the string + // replace it silently instead of complaining + for (char *ptr = pd.label; *ptr == '\0'; ptr++) { + if (*ptr == '\'') { + *ptr = '_'; + } + } + } asprintf(&result, "%s%s", result, pd_value_to_string(pd.value)); @@ -249,7 +260,9 @@ char *mp_range_to_string(const mp_range input) { return result; } -mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { return mp_set_pd_value_double(pd, value); } +mp_perfdata mp_set_pd_value_float(mp_perfdata pd, float value) { + return mp_set_pd_value_double(pd, value); +} mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { pd.value.pd_double = value; @@ -257,15 +270,25 @@ mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) { return pd; } -mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { return mp_set_pd_value_long_long(pd, (long long)value); } +mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { + return mp_set_pd_value_long_long(pd, (long long)value); +} -mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } +mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) { + return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); +} -mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); } +mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { + return mp_set_pd_value_long_long(pd, (long long)value); +} -mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } +mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { + return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); +} -mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { return mp_set_pd_value_long_long(pd, (long long)value); } +mp_perfdata mp_set_pd_value_long(mp_perfdata pd, long value) { + return mp_set_pd_value_long_long(pd, (long long)value); +} mp_perfdata mp_set_pd_value_u_long(mp_perfdata pd, unsigned long value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); @@ -290,19 +313,33 @@ mp_perfdata_value mp_create_pd_value_double(double value) { return res; } -mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); } +mp_perfdata_value mp_create_pd_value_float(float value) { + return mp_create_pd_value_double((double)value); +} -mp_perfdata_value mp_create_pd_value_char(char value) { return mp_create_pd_value_long_long((long long)value); } +mp_perfdata_value mp_create_pd_value_char(char value) { + return mp_create_pd_value_long_long((long long)value); +} -mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } +mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { + return mp_create_pd_value_u_long_long((unsigned long long)value); +} -mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); } +mp_perfdata_value mp_create_pd_value_int(int value) { + return mp_create_pd_value_long_long((long long)value); +} -mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } +mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { + return mp_create_pd_value_u_long_long((unsigned long long)value); +} -mp_perfdata_value mp_create_pd_value_long(long value) { return mp_create_pd_value_long_long((long long)value); } +mp_perfdata_value mp_create_pd_value_long(long value) { + return mp_create_pd_value_long_long((long long)value); +} -mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } +mp_perfdata_value mp_create_pd_value_u_long(unsigned long value) { + return mp_create_pd_value_u_long_long((unsigned long long)value); +} mp_perfdata_value mp_create_pd_value_long_long(long long value) { mp_perfdata_value res = {0}; @@ -368,6 +405,13 @@ mp_range_parsed mp_parse_range_string(const char *input) { } char *working_copy = strdup(input); + if (working_copy == NULL) { + // strdup error, probably + mp_range_parsed result = { + .error = MP_RANGE_PARSING_FAILURE, + }; + return result; + } input = working_copy; char *separator = index(working_copy, ':'); -- cgit v1.2.3-74-g34f1 From 2aabc4d4904a5a0994cac661a20c27ec3d1c79ee Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:04:23 +0200 Subject: Fix spelling ... --- plugins/check_snmp.d/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_snmp.d/config.h b/plugins/check_snmp.d/config.h index a5d5aa52..e7b6d1b3 100644 --- a/plugins/check_snmp.d/config.h +++ b/plugins/check_snmp.d/config.h @@ -65,7 +65,7 @@ typedef struct { // Modify output bool use_oid_as_perf_data_label; - // activate rate calucation + // activate rate calculation bool calculate_rate; unsigned int rate_multiplier; } check_snmp_evaluation_parameters; -- cgit v1.2.3-74-g34f1 From c38276853057e2152de5106fc1de9c002b7d964b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:35:02 +0200 Subject: Little adaptions for old compilers --- plugins/check_snmp.d/check_snmp_helpers.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index 9fa396d8..e18da4a3 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -273,8 +273,7 @@ snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) { } result.response_values[loop_index].value.doubleVal = *(vars->val.doubleVal); result.response_values[loop_index].type = vars->type; - break; - } + } break; case ASN_IPADDRESS: if (verbose) { printf("Debug: Got an IP address\n"); @@ -282,13 +281,13 @@ snmp_responces do_snmp_query(check_snmp_config_snmp_parameters parameters) { result.response_values[loop_index].type = vars->type; // TODO: print address here, state always ok? or regex match? - continue; + break; default: if (verbose) { printf("Debug: Got a unmatched result type: %hhu\n", vars->type); } // TODO: Error here? - continue; + break; } } -- cgit v1.2.3-74-g34f1 From 76e1a1fa8cbb11bc67fae55a0ae2b596faa66444 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:18:21 +0200 Subject: Activate mib parsing in Debian CI pipeline --- .github/prepare_debian.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/prepare_debian.sh b/.github/prepare_debian.sh index b5cacd4a..cffe98c5 100755 --- a/.github/prepare_debian.sh +++ b/.github/prepare_debian.sh @@ -112,6 +112,8 @@ mkdir -p /var/lib/snmp/mib_indexes sed -e 's/^agentaddress.*/agentaddress 127.0.0.1/' -i /etc/snmp/snmpd.conf service snmpd start +sed 's/^mibs ://' -i /etc/snmp/snmp.conf + # start cron, will be used by check_nagios cron -- cgit v1.2.3-74-g34f1 From 2192c6a8a108fc3586ef0bd8e990d7217d2689dc Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:27:27 +0200 Subject: Add break statement to switch path --- plugins/check_snmp.d/check_snmp_helpers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/check_snmp.d/check_snmp_helpers.c b/plugins/check_snmp.d/check_snmp_helpers.c index e18da4a3..ecbfc5dd 100644 --- a/plugins/check_snmp.d/check_snmp_helpers.c +++ b/plugins/check_snmp.d/check_snmp_helpers.c @@ -533,6 +533,7 @@ check_snmp_evaluation evaluate_single_unit(response_value response, } break; case ASN_IPADDRESS: // TODO + break; } if (got_a_numerical_value) { -- cgit v1.2.3-74-g34f1 From c43f845c22a9e879546472aa9051d7ca0803ef2a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:43:27 +0200 Subject: Adjust number of tests --- plugins/t/check_snmp.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/t/check_snmp.t b/plugins/t/check_snmp.t index 99e01add..8d435df3 100644 --- a/plugins/t/check_snmp.t +++ b/plugins/t/check_snmp.t @@ -10,7 +10,7 @@ use NPTest; BEGIN { plan skip_all => 'check_snmp is not compiled' unless -x "./check_snmp"; - plan tests => 42; + plan tests => 62; } my $res; -- cgit v1.2.3-74-g34f1 From 94ae1eccbc35b6fdb5274dbb6b15f0479e68aeed Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 02:07:55 +0200 Subject: Fix some include paths --- plugins/check_curl.d/config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 43be0306..97fbcaab 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -1,9 +1,9 @@ #pragma once #include "../../config.h" -#include "common.h" -#include "states.h" -#include "thresholds.h" +#include "../common.h" +#include "../../lib/states.h" +#include "../../lib/thresholds.h" #include #include #include -- cgit v1.2.3-74-g34f1 From 40b062f1bd77d60279555a2f40f853d829b15db6 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:12:03 +0200 Subject: check_curl: more refactoring --- plugins/check_curl.c | 787 +++++++++++++++++++++++------------------- plugins/check_curl.d/config.h | 48 ++- 2 files changed, 469 insertions(+), 366 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 94b726d0..76dcbfd8 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -115,21 +115,37 @@ enum { #include "regex.h" // Globals -static bool curl_global_initialized = false; -static bool curl_easy_initialized = false; static int verbose = 0; -static bool body_buf_initialized = false; -static curlhelp_write_curlbuf body_buf; -static bool header_buf_initialized = false; -static curlhelp_write_curlbuf header_buf; -static bool status_line_initialized = false; -static curlhelp_statusline status_line; -static bool put_buf_initialized = false; -static curlhelp_read_curlbuf put_buf; - -static CURL *curl; -static struct curl_slist *header_list = NULL; -static long code; + +typedef struct { + bool curl_global_initialized; + bool curl_easy_initialized; + bool body_buf_initialized; + curlhelp_write_curlbuf body_buf; + bool header_buf_initialized; + curlhelp_write_curlbuf header_buf; + bool status_line_initialized; + curlhelp_statusline status_line; + bool put_buf_initialized; + curlhelp_read_curlbuf put_buf; + CURL *curl; +} check_curl_global_state; + +check_curl_global_state global_state = { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .body_buf_initialized = false, + .body_buf = {}, + .header_buf_initialized = false, + .header_buf = {}, + .status_line_initialized = false, + .status_line = {}, + .put_buf_initialized = false, + .put_buf = {}, + .curl = NULL, +}; + +static long httpReturnCode; static char errbuf[MAX_INPUT_BUFFER]; static char msg[DEFAULT_BUFFER_SIZE]; typedef union { @@ -143,8 +159,6 @@ static bool add_sslctx_verify_fun = false; static X509 *cert = NULL; #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; - typedef struct { int errorcode; check_curl_config config; @@ -152,10 +166,16 @@ typedef struct { static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static void handle_curl_option_return_code(CURLcode res, const char *option); -static mp_state_enum check_http(check_curl_config /*config*/, int redir_depth); +static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, + int redir_depth, struct curl_slist *header_list); -static void redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth); +typedef struct { + int redir_depth; + check_curl_working_state working_state; + int error_code; +} redir_wrapper; +static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, + int redir_depth, check_curl_working_state working_state); static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); @@ -218,14 +238,21 @@ int main(int argc, char **argv) { } if (config.display_html) { - printf("", config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, - config.virtual_port ? config.virtual_port : config.server_port, config.server_url); + printf("", + config.initial_config.use_ssl ? "https" : "http", + config.initial_config.host_name ? config.initial_config.host_name + : config.initial_config.server_address, + config.initial_config.virtualPort ? config.initial_config.virtualPort + : config.initial_config.serverPort, + config.initial_config.server_url); } int redir_depth = 0; - exit((int)check_http(config, redir_depth)); + check_curl_working_state working_state = config.initial_config; + struct curl_slist *header_list = NULL; + + exit((int)check_http(config, working_state, redir_depth, header_list)); } #ifdef HAVE_SSL @@ -243,13 +270,11 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { # endif if (verbose >= 2) { puts("* SSL verify callback with certificate:"); - X509_NAME *subject; - X509_NAME *issuer; printf("* issuer:\n"); - issuer = X509_get_issuer_name(cert); + X509_NAME *issuer = X509_get_issuer_name(cert); X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); printf("* curl verify_callback:\n* subject:\n"); - subject = X509_get_subject_name(cert); + X509_NAME *subject = X509_get_subject_name(cert); X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); puts(""); } @@ -305,9 +330,8 @@ static bool expected_statuscode(const char *reply, const char *statuscodes) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); } - char *code; bool result = false; - for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { + for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { if (strstr(reply, code) != NULL) { result = true; break; @@ -328,11 +352,11 @@ void handle_curl_option_return_code(CURLcode res, const char *option) { } int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = addr_family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags |= AI_CANONNAME; + struct addrinfo hints = { + .ai_family = addr_family, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + }; struct addrinfo *result; int errcode = getaddrinfo(host, NULL, &hints, &result); @@ -383,103 +407,115 @@ int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_fam } static void cleanup(void) { - if (status_line_initialized) { - curlhelp_free_statusline(&status_line); + if (global_state.status_line_initialized) { + curlhelp_free_statusline(&global_state.status_line); } - status_line_initialized = false; - if (curl_easy_initialized) { - curl_easy_cleanup(curl); + global_state.status_line_initialized = false; + + if (global_state.curl_easy_initialized) { + curl_easy_cleanup(global_state.curl); } - curl_easy_initialized = false; - if (curl_global_initialized) { + global_state.curl_easy_initialized = false; + + if (global_state.curl_global_initialized) { curl_global_cleanup(); } - curl_global_initialized = false; - if (body_buf_initialized) { - curlhelp_freewritebuffer(&body_buf); + global_state.curl_global_initialized = false; + + if (global_state.body_buf_initialized) { + curlhelp_freewritebuffer(&global_state.body_buf); } - body_buf_initialized = false; - if (header_buf_initialized) { - curlhelp_freewritebuffer(&header_buf); + global_state.body_buf_initialized = false; + + if (global_state.header_buf_initialized) { + curlhelp_freewritebuffer(&global_state.header_buf); } - header_buf_initialized = false; - if (put_buf_initialized) { - curlhelp_freereadbuffer(&put_buf); + global_state.header_buf_initialized = false; + + if (global_state.put_buf_initialized) { + curlhelp_freereadbuffer(&global_state.put_buf); } - put_buf_initialized = false; + global_state.put_buf_initialized = false; } -mp_state_enum check_http(check_curl_config config, int redir_depth) { +mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, + int redir_depth, struct curl_slist *header_list) { /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); } - curl_global_initialized = true; + global_state.curl_global_initialized = true; - if ((curl = curl_easy_init()) == NULL) { + if ((global_state.curl = curl_easy_init()) == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); } - curl_easy_initialized = true; + global_state.curl_easy_initialized = true; /* register cleanup function to shut down libcurl properly */ atexit(cleanup); if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); } /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_STDERR, stdout), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); if (config.automatic_decompression) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""), - "CURLOPT_ACCEPT_ENCODING"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_ACCEPT_ENCODING, ""), + "CURLOPT_ACCEPT_ENCODING"); #else - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ENCODING, ""), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ } /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&body_buf) < 0) { + if (curlhelp_initwritebuffer(&global_state.body_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); } - body_buf_initialized = true; + global_state.body_buf_initialized = true; handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), + curl_easy_setopt(global_state.curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&body_buf), - "CURLOPT_WRITEDATA"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_WRITEDATA, (void *)&global_state.body_buf), + "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&header_buf) < 0) { + if (curlhelp_initwritebuffer(&global_state.header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); } - header_buf_initialized = true; + global_state.header_buf_initialized = true; + handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), + curl_easy_setopt(global_state.curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)&header_buf), - "CURLOPT_WRITEHEADER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_WRITEHEADER, (void *)&global_state.header_buf), + "CURLOPT_WRITEHEADER"); /* set the error buffer */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf), + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER"); /* set timeouts */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + curl_easy_setopt(global_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_TIMEOUT, config.socket_timeout), - "CURLOPT_TIMEOUT"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), + "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ if (config.haproxy_protocol) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L), - "CURLOPT_HAPROXYPROTOCOL"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), + "CURLOPT_HAPROXYPROTOCOL"); } // fill dns resolve cache to make curl connect to the given server_address instead of the @@ -487,19 +523,19 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { struct curl_slist *host = NULL; char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; - if (config.use_ssl && config.host_name != NULL) { + if (workingState.use_ssl && workingState.host_name != NULL) { int res; - if ((res = lookup_host(config.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + if ((res = lookup_host(workingState.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, config.sin_family)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - config.server_address, res, gai_strerror(res)); + workingState.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", config.host_name, config.server_port, - addrstr); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", workingState.host_name, + workingState.serverPort, addrstr); host = curl_slist_append(NULL, dnscache); - curl_easy_setopt(curl, CURLOPT_RESOLVE, host); + curl_easy_setopt(global_state.curl, CURLOPT_RESOLVE, host); if (verbose >= 1) { printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); } @@ -507,66 +543,73 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { // If server_address is an IPv6 address it must be surround by square brackets struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, config.server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(config.server_address) + 3); + if (inet_pton(AF_INET6, workingState.server_address, &tmp_in_addr) == 1) { + char *new_server_address = malloc(strlen(workingState.server_address) + 3); if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(config.server_address) + 3, "[%s]", - config.server_address); - free(config.server_address); - config.server_address = new_server_address; + snprintf(new_server_address, strlen(workingState.server_address) + 3, "[%s]", + workingState.server_address); + free(workingState.server_address); + workingState.server_address = new_server_address; } /* compose URL: use the address we want to connect to, set Host: header later */ char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", config.use_ssl ? "https" : "http", - (config.use_ssl & (config.host_name != NULL)) ? config.host_name - : config.server_address, - config.server_port, config.server_url); + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http", + (workingState.use_ssl & (workingState.host_name != NULL)) + ? workingState.host_name + : workingState.server_address, + workingState.serverPort, workingState.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); } - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, url), "CURLOPT_URL"); + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_URL, url), + "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(config.http_method, "CONNECT") || - strstr(config.server_url, "http") == config.server_url) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_PROXY, config.server_address), - "CURLOPT_PROXY"); + if (!strcmp(workingState.http_method, "CONNECT") || + strstr(workingState.server_url, "http") == workingState.server_url) { + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_PROXY, workingState.server_address), + "CURLOPT_PROXY"); handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long)config.server_port), + curl_easy_setopt(global_state.curl, CURLOPT_PROXYPORT, (long)workingState.serverPort), "CURLOPT_PROXYPORT"); if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", config.server_address, config.server_port); + printf("* curl CURLOPT_PROXY: %s:%d\n", workingState.server_address, + workingState.serverPort); } - config.http_method = "GET"; - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_URL, config.server_url), - "CURLOPT_URL"); + workingState.http_method = "GET"; + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_URL, workingState.server_url), + "CURLOPT_URL"); } /* disable body for HEAD request */ - if (config.http_method && !strcmp(config.http_method, "HEAD")) { - config.no_body = true; + if (workingState.http_method && !strcmp(workingState.http_method, "HEAD")) { + workingState.no_body = true; } /* set HTTP protocol version */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + curl_easy_setopt(global_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); /* set HTTP method */ - if (config.http_method) { - if (!strcmp(config.http_method, "POST")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_POST, 1), "CURLOPT_POST"); - } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_UPLOAD, 1), + if (workingState.http_method) { + if (!strcmp(workingState.http_method, "POST")) { + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POST, 1), + "CURLOPT_POST"); + } else if (!strcmp(workingState.http_method, "PUT")) { + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, config.http_method), - "CURLOPT_CUSTOMREQUEST"); + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + CURLOPT_CUSTOMREQUEST, + workingState.http_method), + "CURLOPT_CUSTOMREQUEST"); } } @@ -583,13 +626,13 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in * anyway */ char http_header[DEFAULT_BUFFER_SIZE]; - if (config.host_name != NULL && force_host_header == NULL) { - if ((config.virtual_port != HTTP_PORT && !config.use_ssl) || - (config.virtual_port != HTTPS_PORT && config.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", config.host_name, - config.virtual_port); + if (workingState.host_name != NULL && force_host_header == NULL) { + if ((workingState.virtualPort != HTTP_PORT && !workingState.use_ssl) || + (workingState.virtualPort != HTTPS_PORT && workingState.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", workingState.host_name, + workingState.virtualPort); } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", config.host_name); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", workingState.host_name); } header_list = curl_slist_append(header_list, http_header); } @@ -604,57 +647,63 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { for (size_t i = 0; i < config.http_opt_headers_count; i++) { header_list = curl_slist_append(header_list, config.http_opt_headers[i]); } - /* This cannot be free'd here because a redirection will then try to access this and - * segfault */ - /* Covered in a testcase in tests/check_http.t */ - /* free(http_opt_headers); */ } /* set HTTP headers */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list), - "CURLOPT_HTTPHEADER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ - if (config.use_ssl) { + if (workingState.use_ssl) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); + curl_easy_setopt(global_state.curl, CURLOPT_SSLVERSION, config.ssl_version), + "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ if (config.client_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSLCERT, config.client_cert), - "CURLOPT_SSLCERT"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSLCERT, config.client_cert), + "CURLOPT_SSLCERT"); } + if (config.client_privkey) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); + curl_easy_setopt(global_state.curl, CURLOPT_SSLKEY, config.client_privkey), + "CURLOPT_SSLKEY"); } + if (config.ca_cert) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CAINFO, config.ca_cert), - "CURLOPT_CAINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); } + if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2), - "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks * TODO: depending on more options have aspects we want * to be less tolerant about ssl verfications */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0), - "CURLOPT_SSL_VERIFYHOST"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + "CURLOPT_SSL_VERIFYHOST"); } /* detect SSL library used by libcurl */ - ssl_library = curlhelp_get_ssl_library(); + curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); /* try hard to get a stack of certificates to verify against */ if (config.check_cert) { @@ -672,15 +721,15 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { # endif /* USE_OPENSSL */ /* libcurl is built with OpenSSL, monitoring plugins, so falling * back to manually extracting certificate information */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), - "CURLOPT_CERTINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); break; case CURLHELP_SSL_LIBRARY_NSS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) /* NSS: support for CERTINFO is implemented since 7.34.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), - "CURLOPT_CERTINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -692,8 +741,8 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { case CURLHELP_SSL_LIBRARY_GNUTLS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L), - "CURLOPT_CERTINFO"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -726,29 +775,33 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { # if LIBCURL_VERSION_NUM >= \ MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), - "CURLOPT_SSL_CTX_FUNCTION"); + if (curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != + CURLE_UNKNOWN_OPTION) { + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + "CURLOPT_SSL_CTX_FUNCTION"); } # endif #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERAGENT, config.user_agent), - "CURLOPT_USERAGENT"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_USERAGENT, config.user_agent), + "CURLOPT_USERAGENT"); /* proxy-authentication */ if (strcmp(config.proxy_auth, "")) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + curl_easy_setopt(global_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); } /* authentication */ if (strcmp(config.user_auth, "")) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_USERPWD, config.user_auth), - "CURLOPT_USERPWD"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_USERPWD, config.user_auth), + "CURLOPT_USERPWD"); } /* TODO: parameter auth method, bitfield of following methods: @@ -770,25 +823,27 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* handle redirections */ if (config.onredirect == STATE_DEPENDENT) { if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1), - "CURLOPT_FOLLOWLOCATION"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_FOLLOWLOCATION, 1), + "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, config.max_depth + 1), + curl_easy_setopt(global_state.curl, CURLOPT_MAXREDIRS, config.max_depth + 1), "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + curl_easy_setopt(global_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), "CURLOPT_REDIR_PROTOCOLS_STR"); #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), - "CURLOPT_REDIRECT_PROTOCOLS"); + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + CURLOPT_REDIR_PROTOCOLS, + CURLPROTO_HTTP | CURLPROTO_HTTPS), + "CURLOPT_REDIRECT_PROTOCOLS"); #endif /* TODO: handle the following aspects of redirection, make them @@ -805,28 +860,31 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* no-body */ - if (config.no_body) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); + if (workingState.no_body) { + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_NOBODY, 1), + "CURLOPT_NOBODY"); } /* IPv4 or IPv6 forced DNS resolution */ if (config.sin_family == AF_UNSPEC) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); } else if (config.sin_family == AF_INET) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) else if (config.sin_family == AF_INET6) { - handle_curl_option_return_code(curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); + handle_curl_option_return_code( + curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); } #endif /* either send http POST data (any data, not only POST)*/ - if (!strcmp(config.http_method, "POST") || !strcmp(config.http_method, "PUT")) { + if (!strcmp(workingState.http_method, "POST") || !strcmp(workingState.http_method, "PUT")) { /* set content of payload for POST and PUT */ if (config.http_content_type) { snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", @@ -835,30 +893,32 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!config.http_post_data) { - config.http_post_data = ""; + if (!workingState.http_post_data) { + workingState.http_post_data = ""; } - if (!strcmp(config.http_method, "POST")) { + if (!strcmp(workingState.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POSTFIELDS, + workingState.http_post_data), + "CURLOPT_POSTFIELDS"); + } else if (!strcmp(workingState.http_method, "PUT")) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config.http_post_data), - "CURLOPT_POSTFIELDS"); - } else if (!strcmp(config.http_method, "PUT")) { - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_READFUNCTION, + curl_easy_setopt(global_state.curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&put_buf, config.http_post_data, - strlen(config.http_post_data)) < 0) { + if (curlhelp_initreadbuffer(&global_state.put_buf, workingState.http_post_data, + strlen(workingState.http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } - put_buf_initialized = true; - handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA"); + global_state.put_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_READDATA, + (void *)&global_state.put_buf), + "CURLOPT_READDATA"); handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_INFILESIZE, - (curl_off_t)strlen(config.http_post_data)), + curl_easy_setopt(global_state.curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(workingState.http_post_data)), "CURLOPT_INFILESIZE"); } } @@ -868,22 +928,22 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* enable reading cookies from a file, and if the filename is an empty string, only * enable the curl cookie engine */ handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + curl_easy_setopt(global_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, * since writing it would fail */ if (*config.cookie_jar_file) { handle_curl_option_return_code( - curl_easy_setopt(curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + curl_easy_setopt(global_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); } } /* do the request */ - CURLcode res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(global_state.curl); - if (verbose >= 2 && config.http_post_data) { - printf("**** REQUEST CONTENT ****\n%s\n", config.http_post_data); + if (verbose >= 2 && workingState.http_post_data) { + printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); } /* free header and server IP resolve lists, we don't need it anymore */ @@ -899,14 +959,14 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { if (res != CURLE_OK) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), - config.server_port, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } mp_state_enum result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL - if (config.use_ssl) { + if (workingState.use_ssl) { if (config.check_cert) { if (is_openssl_callback) { # ifdef USE_OPENSSL @@ -927,7 +987,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { cert_ptr_union cert_ptr = {0}; cert_ptr.to_info = NULL; - res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); + res = curl_easy_getinfo(global_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { # ifdef USE_OPENSSL /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert @@ -994,32 +1054,37 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { * performance data to the answer always */ double total_time; - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time), - "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(&header_buf, &body_buf); + handle_curl_option_return_code( + curl_easy_getinfo(global_state.curl, CURLINFO_TOTAL_TIME, &total_time), + "CURLINFO_TOTAL_TIME"); + size_t page_len = get_content_length(&global_state.header_buf, &global_state.body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + curl_easy_getinfo(global_state.curl, CURLINFO_CONNECT_TIME, &time_connect), + "CURLINFO_CONNECT_TIME"); + double time_appconnect; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), + curl_easy_getinfo(global_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); + double time_headers; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + curl_easy_getinfo(global_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); + double time_firstbyte; handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), + curl_easy_getinfo(global_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", perfd_time(total_time, config.thlds, config.socket_timeout), perfd_size(page_len, config.min_page_len), perfd_time_connect(time_connect, config.socket_timeout), - config.use_ssl + workingState.use_ssl ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) : "", perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), @@ -1032,46 +1097,47 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) { + if (strlen(global_state.header_buf.buf) == 0 && strlen(global_state.body_buf.buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); } /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(header_buf.buf, &status_line) < 0) { + if (curlhelp_parse_statusline(global_state.header_buf.buf, &global_state.status_line) < 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); /* we cannot know the major/minor version here for sure as we cannot parse the first * line */ - die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", code, msg); + die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", httpReturnCode, msg); } - status_line_initialized = true; + global_state.status_line_initialized = true; /* get result code from cURL */ - handle_curl_option_return_code(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code), - "CURLINFO_RESPONSE_CODE"); + handle_curl_option_return_code( + curl_easy_getinfo(global_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), + "CURLINFO_RESPONSE_CODE"); if (verbose >= 2) { - printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", code); + printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode); } /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf, - (config.no_body ? " [[ skipped ]]" : body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", global_state.header_buf.buf, + (workingState.no_body ? " [[ skipped ]]" : global_state.body_buf.buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(status_line.first_line, config.server_expect)) { - if (config.server_port == HTTP_PORT) { + if (!expected_statuscode(global_state.status_line.first_line, config.server_expect)) { + if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - status_line.first_line); + global_state.status_line.first_line); } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), - config.server_port, status_line.first_line); + workingState.serverPort, global_state.status_line.first_line); } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? body_buf.buf : ""); + config.show_body ? global_state.body_buf.buf : ""); } mp_state_enum result = STATE_OK; @@ -1084,26 +1150,29 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { result = STATE_OK; } else { /* illegal return codes result in a critical state */ - if (code >= 600 || code < 100) { + if (httpReturnCode >= 600 || httpReturnCode < 100) { die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - status_line.http_code, status_line.msg); + global_state.status_line.http_code, global_state.status_line.msg); /* server errors result in a critical state */ - } else if (code >= 500) { + } else if (httpReturnCode >= 500) { result = STATE_CRITICAL; /* client errors result in a warning state */ - } else if (code >= 400) { + } else if (httpReturnCode >= 400) { result = STATE_WARNING; /* check redirected page if specified */ - } else if (code >= 300) { + } else if (httpReturnCode >= 300) { if (config.onredirect == STATE_DEPENDENT) { if (config.followmethod == FOLLOW_LIBCURL) { - code = status_line.http_code; + httpReturnCode = global_state.status_line.http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ - redir(&header_buf, config, redir_depth); + redir_wrapper redir_result = + redir(&global_state.header_buf, config, redir_depth, workingState); + check_http(config, redir_result.working_state, redir_result.redir_depth, + header_list); } } else { /* this is a specific code in the command line to @@ -1120,7 +1189,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { /* libcurl redirection internally, handle error states here */ if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + curl_easy_getinfo(global_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); @@ -1133,20 +1202,22 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } /* check status codes, set exit status accordingly */ - if (status_line.http_code != code) { + if (global_state.status_line.http_code != httpReturnCode) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(status_line.http_major, status_line.http_minor), - status_line.http_code, status_line.msg, code); + string_statuscode(global_state.status_line.http_major, + global_state.status_line.http_minor), + global_state.status_line.http_code, global_state.status_line.msg, httpReturnCode); } if (config.maximum_age >= 0) { - result = max_state_alt(check_document_dates(&header_buf, &msg, config.maximum_age), result); + result = max_state_alt( + check_document_dates(&global_state.header_buf, &msg, config.maximum_age), result); } /* Page and Header content checks go here */ if (strlen(config.header_expect)) { - if (!strstr(header_buf.buf, config.header_expect)) { + if (!strstr(global_state.header_buf.buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -1158,9 +1229,9 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), - msg, output_header_search, config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, - config.server_port, config.server_url); + msg, output_header_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); strcpy(msg, tmp); @@ -1169,7 +1240,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { } if (strlen(config.string_expect)) { - if (!strstr(body_buf.buf, config.string_expect)) { + if (!strstr(global_state.body_buf.buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -1181,9 +1252,9 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { char tmp[DEFAULT_BUFFER_SIZE]; snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), - msg, output_string_search, config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, - config.server_port, config.server_url); + msg, output_string_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); strcpy(msg, tmp); @@ -1194,7 +1265,7 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { if (strlen(config.regexp)) { regex_t preg; regmatch_t pmatch[REGS]; - int errcode = regexec(&preg, body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&preg, global_state.body_buf.buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1259,22 +1330,25 @@ mp_state_enum check_http(check_curl_config config, int redir_depth) { * msg); */ die((int)max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", - state_text(result), string_statuscode(status_line.http_major, status_line.http_minor), - status_line.http_code, status_line.msg, strlen(msg) > 0 ? " - " : "", msg, page_len, - total_time, (config.display_html ? "" : ""), perfstring, - (config.show_body ? body_buf.buf : ""), (config.show_body ? "\n" : "")); + state_text(result), + string_statuscode(global_state.status_line.http_major, global_state.status_line.http_minor), + global_state.status_line.http_code, global_state.status_line.msg, + strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, + (config.display_html ? "" : ""), perfstring, + (config.show_body ? global_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } -int uri_strcmp(const UriTextRangeA range, const char *s) { +int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) { if (!range.first) { return -1; } - if ((size_t)(range.afterLast - range.first) < strlen(s)) { + if ((size_t)(range.afterLast - range.first) < strlen(stringToCompare)) { return -1; } - return strncmp(s, range.first, min((size_t)(range.afterLast - range.first), strlen(s))); + return strncmp(stringToCompare, range.first, + min((size_t)(range.afterLast - range.first), strlen(stringToCompare))); } char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { @@ -1287,8 +1361,8 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { return buf; } -void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int redir_depth) { - +redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, + int redir_depth, check_curl_working_state working_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1340,9 +1414,9 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red } if (uri.pathHead) { printf(_("** path: ")); - const UriPathSegmentA *p = uri.pathHead; - for (; p; p = p->next) { - printf("/%s", uri_string(p->text, buf, DEFAULT_BUFFER_SIZE)); + for (UriPathSegmentA *path_segment = uri.pathHead; path_segment; + path_segment = path_segment->next) { + printf("/%s", uri_string(path_segment->text, buf, DEFAULT_BUFFER_SIZE)); } puts(""); } @@ -1355,7 +1429,7 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red } if (uri.scheme.first) { - config.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https")); + working_state.use_ssl = (bool)(!uri_strcmp(uri.scheme, "https")); } /* we do a sloppy test here only, because uriparser would have failed @@ -1366,7 +1440,7 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red new_port = atoi(uri_string(uri.portText, buf, DEFAULT_BUFFER_SIZE)); } else { new_port = HTTP_PORT; - if (config.use_ssl) { + if (working_state.use_ssl) { new_port = HTTPS_PORT; } } @@ -1380,9 +1454,10 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red */ char *new_host; if (!uri.scheme.first && !uri.hostText.first) { - new_host = strdup(config.host_name ? config.host_name : config.server_address); - new_port = config.server_port; - if (config.use_ssl) { + new_host = strdup(working_state.host_name ? working_state.host_name + : working_state.server_address); + new_port = working_state.serverPort; + if (working_state.use_ssl) { uri_string(uri.scheme, "https", DEFAULT_BUFFER_SIZE); } } else { @@ -1393,50 +1468,51 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red /* TODO: handle fragments and query part of URL */ char *new_url = (char *)calloc(1, DEFAULT_BUFFER_SIZE); if (uri.pathHead) { - const UriPathSegmentA *p = uri.pathHead; - for (; p; p = p->next) { + for (UriPathSegmentA *pathSegment = uri.pathHead; pathSegment; + pathSegment = pathSegment->next) { strncat(new_url, "/", DEFAULT_BUFFER_SIZE); - strncat(new_url, uri_string(p->text, buf, DEFAULT_BUFFER_SIZE), + strncat(new_url, uri_string(pathSegment->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE - 1); } } - if (config.server_port == new_port && - !strncmp(config.server_address, new_host, MAX_IPV4_HOSTLENGTH) && - (config.host_name && !strncmp(config.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && - !strcmp(config.server_url, new_url)) { + if (working_state.serverPort == new_port && + !strncmp(working_state.server_address, new_host, MAX_IPV4_HOSTLENGTH) && + (working_state.host_name && + !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && + !strcmp(working_state.server_url, new_url)) { die(STATE_CRITICAL, _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), - config.use_ssl ? "https" : "http", new_host, new_port, new_url, + working_state.use_ssl ? "https" : "http", new_host, new_port, new_url, (config.display_html ? "" : "")); } /* set new values for redirected request */ if (!(config.followsticky & STICKY_HOST)) { - free(config.server_address); - config.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); + free(working_state.server_address); + working_state.server_address = strndup(new_host, MAX_IPV4_HOSTLENGTH); } if (!(config.followsticky & STICKY_PORT)) { - config.server_port = (unsigned short)new_port; + working_state.serverPort = (unsigned short)new_port; } - free(config.host_name); - config.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); + free(working_state.host_name); + working_state.host_name = strndup(new_host, MAX_IPV4_HOSTLENGTH); /* reset virtual port */ - config.virtual_port = config.server_port; + working_state.virtualPort = working_state.serverPort; free(new_host); - free(config.server_url); - config.server_url = new_url; + free(working_state.server_url); + working_state.server_url = new_url; uriFreeUriMembersA(&uri); if (verbose) { - printf(_("Redirection to %s://%s:%d%s\n"), config.use_ssl ? "https" : "http", - config.host_name ? config.host_name : config.server_address, config.server_port, - config.server_url); + printf(_("Redirection to %s://%s:%d%s\n"), working_state.use_ssl ? "https" : "http", + working_state.host_name ? working_state.host_name : working_state.server_address, + working_state.serverPort, working_state.server_url); } /* TODO: the hash component MUST be taken from the original URL and @@ -1444,7 +1520,13 @@ void redir(curlhelp_write_curlbuf *header_buf, check_curl_config config, int red */ cleanup(); - check_http(config, redir_depth); + + redir_wrapper result = { + .redir_depth = redir_depth, + .working_state = working_state, + .error_code = OK, + }; + return result; } /* check whether a file exists */ @@ -1585,31 +1667,34 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { warning_thresholds = optarg; break; case 'H': /* virtual host */ - result.config.host_name = strdup(optarg); - char *p; + result.config.initial_config.host_name = strdup(optarg); + char *tmp_string; size_t host_name_length; - if (result.config.host_name[0] == '[') { - if ((p = strstr(result.config.host_name, "]:")) != NULL) { /* [IPv6]:port */ - result.config.virtual_port = atoi(p + 2); + if (result.config.initial_config.host_name[0] == '[') { + if ((tmp_string = strstr(result.config.initial_config.host_name, "]:")) != + NULL) { /* [IPv6]:port */ + result.config.initial_config.virtualPort = atoi(tmp_string + 2); /* cut off the port */ - host_name_length = strlen(result.config.host_name) - strlen(p) - 1; - free(result.config.host_name); - result.config.host_name = strndup(optarg, host_name_length); + host_name_length = + strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1; + free(result.config.initial_config.host_name); + result.config.initial_config.host_name = strndup(optarg, host_name_length); } - } else if ((p = strchr(result.config.host_name, ':')) != NULL && - strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ - result.config.virtual_port = atoi(p); + } else if ((tmp_string = strchr(result.config.initial_config.host_name, ':')) != NULL && + strchr(++tmp_string, ':') == NULL) { /* IPv4:port or host:port */ + result.config.initial_config.virtualPort = atoi(tmp_string); /* cut off the port */ - host_name_length = strlen(result.config.host_name) - strlen(p) - 1; - free(result.config.host_name); - result.config.host_name = strndup(optarg, host_name_length); + host_name_length = + strlen(result.config.initial_config.host_name) - strlen(tmp_string) - 1; + free(result.config.initial_config.host_name); + result.config.initial_config.host_name = strndup(optarg, host_name_length); } break; case 'I': /* internet address */ - result.config.server_address = strdup(optarg); + result.config.initial_config.server_address = strdup(optarg); break; case 'u': /* URL path */ - result.config.server_url = strdup(optarg); + result.config.initial_config.server_url = strdup(optarg); break; case 'p': /* Server port */ if (!is_intnonneg(optarg)) { @@ -1618,7 +1703,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { if (strtol(optarg, NULL, 10) > MAX_PORT) { usage2(_("Invalid port number, supplied port number is too big"), optarg); } - result.config.server_port = (unsigned short)strtol(optarg, NULL, 10); + result.config.initial_config.serverPort = (unsigned short)strtol(optarg, NULL, 10); specify_port = true; } break; @@ -1631,18 +1716,18 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ - if (!result.config.http_post_data) { - result.config.http_post_data = strdup(optarg); + if (!result.config.initial_config.http_post_data) { + result.config.initial_config.http_post_data = strdup(optarg); } - if (!result.config.http_method) { - result.config.http_method = strdup("POST"); + if (!result.config.initial_config.http_method) { + result.config.initial_config.http_method = strdup("POST"); } break; case 'j': /* Set HTTP method */ - if (result.config.http_method) { - free(result.config.http_method); + if (result.config.initial_config.http_method) { + free(result.config.initial_config.http_method); } - result.config.http_method = strdup(optarg); + result.config.initial_config.http_method = strdup(optarg); break; case 'A': /* useragent */ strncpy(result.config.user_agent, optarg, DEFAULT_BUFFER_SIZE); @@ -1723,7 +1808,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { { enable_ssl: bool got_plus = false; - result.config.use_ssl = true; + result.config.initial_config.use_ssl = true; /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. * Only set if it's non-zero. This helps when we include multiple * parameters, like -S and -C combinations */ @@ -1801,7 +1886,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); } if (!specify_port) { - result.config.server_port = HTTPS_PORT; + result.config.initial_config.serverPort = HTTPS_PORT; } } break; #else /* LIBCURL_FEATURE_SSL */ @@ -1935,7 +2020,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; } case 'N': /* no-body */ - result.config.no_body = true; + result.config.initial_config.no_body = true; break; case 'M': /* max-age */ { @@ -1996,21 +2081,22 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } } - int c = optind; + int option_counter = optind; - if (result.config.server_address == NULL && c < argc) { - result.config.server_address = strdup(argv[c++]); + if (result.config.initial_config.server_address == NULL && option_counter < argc) { + result.config.initial_config.server_address = strdup(argv[option_counter++]); } - if (result.config.host_name == NULL && c < argc) { - result.config.host_name = strdup(argv[c++]); + if (result.config.initial_config.host_name == NULL && option_counter < argc) { + result.config.initial_config.host_name = strdup(argv[option_counter++]); } - if (result.config.server_address == NULL) { - if (result.config.host_name == NULL) { + if (result.config.initial_config.server_address == NULL) { + if (result.config.initial_config.host_name == NULL) { usage4(_("You must specify a server address or host name")); } else { - result.config.server_address = strdup(result.config.host_name); + result.config.initial_config.server_address = + strdup(result.config.initial_config.host_name); } } @@ -2024,21 +2110,23 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { printf("* Socket timeout set to %ld seconds\n", result.config.socket_timeout); } - if (result.config.http_method == NULL) { - result.config.http_method = strdup("GET"); + if (result.config.initial_config.http_method == NULL) { + result.config.initial_config.http_method = strdup("GET"); } if (result.config.client_cert && !result.config.client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); } - if (result.config.virtual_port == 0) { - result.config.virtual_port = result.config.server_port; + if (result.config.initial_config.virtualPort == 0) { + result.config.initial_config.virtualPort = result.config.initial_config.serverPort; } else { - if ((result.config.use_ssl && result.config.server_port == HTTPS_PORT) || - (!result.config.use_ssl && result.config.server_port == HTTP_PORT)) { + if ((result.config.initial_config.use_ssl && + result.config.initial_config.serverPort == HTTPS_PORT) || + (!result.config.initial_config.use_ssl && + result.config.initial_config.serverPort == HTTP_PORT)) { if (!specify_port) { - result.config.server_port = result.config.virtual_port; + result.config.initial_config.serverPort = result.config.initial_config.virtualPort; } } } @@ -2389,12 +2477,12 @@ size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, v size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; - size_t n = min(nmemb * size, buf->buflen - buf->pos); + size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos); - memcpy(buffer, buf->buf + buf->pos, n); - buf->pos += n; + memcpy(buffer, buf->buf + buf->pos, minimalSize); + buf->pos += minimalSize; - return n; + return minimalSize; } void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { @@ -2473,86 +2561,85 @@ int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) char *first_line_buf = strdup(status_line->first_line); /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ - char *p = strtok(first_line_buf, "/"); - if (p == NULL) { + char *temp_string = strtok(first_line_buf, "/"); + if (temp_string == NULL) { free(first_line_buf); return -1; } - if (strcmp(p, "HTTP") != 0) { + if (strcmp(temp_string, "HTTP") != 0) { free(first_line_buf); return -1; } - p = strtok(NULL, " "); - if (p == NULL) { + temp_string = strtok(NULL, " "); + if (temp_string == NULL) { free(first_line_buf); return -1; } - char *pp; - if (strchr(p, '.') != NULL) { + char *temp_string_2; + if (strchr(temp_string, '.') != NULL) { /* HTTP 1.x case */ - strtok(p, "."); - status_line->http_major = (int)strtol(p, &pp, 10); - if (*pp != '\0') { + strtok(temp_string, "."); + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } strtok(NULL, " "); - status_line->http_minor = (int)strtol(p, &pp, 10); - if (*pp != '\0') { + status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } - p += 4; /* 1.x SP */ + temp_string += 4; /* 1.x SP */ } else { /* HTTP 2 case */ - status_line->http_major = (int)strtol(p, &pp, 10); + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); status_line->http_minor = 0; - p += 2; /* 2 SP */ + temp_string += 2; /* 2 SP */ } /* status code: "404" or "404.1", then SP */ - - p = strtok(p, " "); - if (p == NULL) { + temp_string = strtok(temp_string, " "); + if (temp_string == NULL) { free(first_line_buf); return -1; } - if (strchr(p, '.') != NULL) { + if (strchr(temp_string, '.') != NULL) { char *ppp; - ppp = strtok(p, "."); - status_line->http_code = (int)strtol(ppp, &pp, 10); - if (*pp != '\0') { + ppp = strtok(temp_string, "."); + status_line->http_code = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } ppp = strtok(NULL, ""); - status_line->http_subcode = (int)strtol(ppp, &pp, 10); - if (*pp != '\0') { + status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } - p += 6; /* 400.1 SP */ + temp_string += 6; /* 400.1 SP */ } else { - status_line->http_code = (int)strtol(p, &pp, 10); + status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10); status_line->http_subcode = -1; - if (*pp != '\0') { + if (*temp_string_2 != '\0') { free(first_line_buf); return -1; } - p += 4; /* 400 SP */ + temp_string += 4; /* 400 SP */ } /* Human readable message: "Not Found" CRLF */ - p = strtok(p, ""); - if (p == NULL) { + temp_string = strtok(temp_string, ""); + if (temp_string == NULL) { status_line->msg = ""; return 0; } - status_line->msg = status_line->first_line + (p - first_line_buf); + status_line->msg = status_line->first_line + (temp_string - first_line_buf); free(first_line_buf); return 0; diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 97fbcaab..9de68713 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -34,10 +34,37 @@ enum { typedef struct { char *server_address; - unsigned short server_port; - unsigned short virtual_port; - char *host_name; char *server_url; + char *host_name; + + char *http_method; + + char *http_post_data; + + unsigned short virtualPort; + unsigned short serverPort; + + bool use_ssl; + bool no_body; +} check_curl_working_state; + +check_curl_working_state check_curl_working_state_init() { + check_curl_working_state result = { + .server_address = NULL, + .server_url = DEFAULT_SERVER_URL, + .host_name = NULL, + .http_method = NULL, + .http_post_data = NULL, + .virtualPort = 0, + .serverPort = 0, + .use_ssl = false, + .no_body = false, + }; + return result; +} + +typedef struct { + check_curl_working_state initial_config; sa_family_t sin_family; bool automatic_decompression; @@ -45,13 +72,10 @@ typedef struct { char *client_cert; char *client_privkey; char *ca_cert; - bool use_ssl; int ssl_version; - char *http_method; char user_agent[DEFAULT_BUFFER_SIZE]; char **http_opt_headers; size_t http_opt_headers_count; - char *http_post_data; int max_depth; char *http_content_type; long socket_timeout; @@ -59,7 +83,6 @@ typedef struct { char proxy_auth[MAX_INPUT_BUFFER]; int followmethod; int followsticky; - bool no_body; int curl_http_version; char *cookie_jar_file; @@ -88,11 +111,8 @@ typedef struct { check_curl_config check_curl_config_init() { check_curl_config tmp = { - .server_address = NULL, - .server_port = HTTP_PORT, - .virtual_port = 0, - .host_name = NULL, - .server_url = strdup(DEFAULT_SERVER_URL), + .initial_config = check_curl_working_state_init(), + .sin_family = AF_UNSPEC, .automatic_decompression = false, @@ -100,13 +120,10 @@ check_curl_config check_curl_config_init() { .client_cert = NULL, .client_privkey = NULL, .ca_cert = NULL, - .use_ssl = false, .ssl_version = CURL_SSLVERSION_DEFAULT, - .http_method = NULL, .user_agent = {'\0'}, .http_opt_headers = NULL, .http_opt_headers_count = 0, - .http_post_data = NULL, .max_depth = DEFAULT_MAX_REDIRS, .http_content_type = NULL, .socket_timeout = DEFAULT_SOCKET_TIMEOUT, @@ -114,7 +131,6 @@ check_curl_config check_curl_config_init() { .proxy_auth = "", .followmethod = FOLLOW_HTTP_CURL, .followsticky = STICKY_NONE, - .no_body = false, .curl_http_version = CURL_HTTP_VERSION_NONE, .cookie_jar_file = NULL, -- cgit v1.2.3-74-g34f1 From 75f04ab4b0e96e9c32002f11e2ae4d4b385d5bc3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:20:29 +0200 Subject: check_http: formatting + no-brainer linter fixes --- plugins/check_http.c | 3589 +++++++++++++++++++++++++------------------------- 1 file changed, 1822 insertions(+), 1767 deletions(-) diff --git a/plugins/check_http.c b/plugins/check_http.c index 8e0c15ec..d264b95d 100644 --- a/plugins/check_http.c +++ b/plugins/check_http.c @@ -1,35 +1,35 @@ /***************************************************************************** -* -* Monitoring check_http plugin -* -* License: GPL -* Copyright (c) 1999-2024 Monitoring Plugins Development Team -* -* Description: -* -* This file contains the check_http plugin -* -* This plugin tests the HTTP service on the specified host. It can test -* normal (http) and secure (https) servers, follow redirects, search for -* strings and regular expressions, check connection times, and report on -* certificate expiration times. -* -* -* 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_http plugin + * + * License: GPL + * Copyright (c) 1999-2024 Monitoring Plugins Development Team + * + * Description: + * + * This file contains the check_http plugin + * + * This plugin tests the HTTP service on the specified host. It can test + * normal (http) and secure (https) servers, follow redirects, search for + * strings and regular expressions, check connection times, and report on + * certificate expiration times. + * + * + * 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 . + * + * + *****************************************************************************/ const char *progname = "check_http"; const char *copyright = "1999-2024"; @@ -41,7 +41,6 @@ const char *email = "devel@monitoring-plugins.org"; #include "base64.h" #include "netutils.h" #include "utils.h" -#include "base64.h" #include #define STICKY_NONE 0 @@ -50,1346 +49,1394 @@ const char *email = "devel@monitoring-plugins.org"; #define HTTP_EXPECT "HTTP/1." enum { - MAX_IPV4_HOSTLENGTH = 255, - HTTP_PORT = 80, - HTTPS_PORT = 443, - MAX_PORT = 65535, - DEFAULT_MAX_REDIRS = 15 + MAX_IPV4_HOSTLENGTH = 255, + HTTP_PORT = 80, + HTTPS_PORT = 443, + MAX_PORT = 65535, + DEFAULT_MAX_REDIRS = 15 }; #ifdef HAVE_SSL -bool check_cert = false; -bool continue_after_check_cert = false; -int ssl_version = 0; -int days_till_exp_warn, days_till_exp_crit; -char *randbuff; -X509 *server_cert; -# define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) -# define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) +static bool check_cert = false; +static bool continue_after_check_cert = false; +static int ssl_version = 0; +static int days_till_exp_warn, days_till_exp_crit; +# define my_recv(buf, len) ((use_ssl) ? np_net_ssl_read(buf, len) : read(sd, buf, len)) +# define my_send(buf, len) ((use_ssl) ? np_net_ssl_write(buf, len) : send(sd, buf, len, 0)) #else /* ifndef HAVE_SSL */ -# define my_recv(buf, len) read(sd, buf, len) -# define my_send(buf, len) send(sd, buf, len, 0) +# define my_recv(buf, len) read(sd, buf, len) +# define my_send(buf, len) send(sd, buf, len, 0) #endif /* HAVE_SSL */ -bool no_body = false; -int maximum_age = -1; +static bool no_body = false; +static int maximum_age = -1; enum { - REGS = 2, - MAX_RE_SIZE = 1024 + REGS = 2, + MAX_RE_SIZE = 1024 }; #include "regex.h" -regex_t preg; -regmatch_t pmatch[REGS]; -char regexp[MAX_RE_SIZE]; -char errbuf[MAX_INPUT_BUFFER]; -int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; -int errcode; -int invert_regex = 0; -int state_regex = STATE_CRITICAL; - -struct timeval tv; -struct timeval tv_temp; +static regex_t preg; +static regmatch_t pmatch[REGS]; +static char regexp[MAX_RE_SIZE]; +static char errbuf[MAX_INPUT_BUFFER]; +static int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; +static int errcode; +static int invert_regex = 0; +static int state_regex = STATE_CRITICAL; + +static struct timeval tv; +static struct timeval tv_temp; #define HTTP_URL "/" -#define CRLF "\r\n" - -bool specify_port = false; -int server_port = HTTP_PORT; -int virtual_port = 0; -char server_port_text[6] = ""; -char server_type[6] = "http"; -char *server_address; -char *host_name; -int host_name_length; -char *server_url; -char *user_agent; -int server_url_length; -int server_expect_yn = 0; -char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; -char header_expect[MAX_INPUT_BUFFER] = ""; -char string_expect[MAX_INPUT_BUFFER] = ""; -char *warning_thresholds = NULL; -char *critical_thresholds = NULL; -thresholds *thlds; -char user_auth[MAX_INPUT_BUFFER] = ""; -char proxy_auth[MAX_INPUT_BUFFER] = ""; -bool display_html = false; -char **http_opt_headers; -int http_opt_headers_count = 0; -int onredirect = STATE_OK; -int followsticky = STICKY_NONE; -bool use_ssl = false; -bool use_sni = false; -bool verbose = false; -bool show_extended_perfdata = false; -bool show_body = false; -int sd; -int min_page_len = 0; -int max_page_len = 0; -int redir_depth = 0; -int max_depth = DEFAULT_MAX_REDIRS; -char *http_method; -char *http_method_proxy; -char *http_post_data; -char *http_content_type; -char buffer[MAX_INPUT_BUFFER]; -char *client_cert = NULL; -char *client_privkey = NULL; +#define CRLF "\r\n" + +static bool specify_port = false; +static int server_port = HTTP_PORT; +static int virtual_port = 0; +static char server_type[6] = "http"; +static char *server_address; +static char *host_name; +static int host_name_length; +static char *server_url; +static char *user_agent; +static int server_url_length; +static int server_expect_yn = 0; +static char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT; +static char header_expect[MAX_INPUT_BUFFER] = ""; +static char string_expect[MAX_INPUT_BUFFER] = ""; +static char *warning_thresholds = NULL; +static char *critical_thresholds = NULL; +static thresholds *thlds; +static char user_auth[MAX_INPUT_BUFFER] = ""; +static char proxy_auth[MAX_INPUT_BUFFER] = ""; +static bool display_html = false; +static char **http_opt_headers; +static int http_opt_headers_count = 0; +static int onredirect = STATE_OK; +static int followsticky = STICKY_NONE; +static bool use_ssl = false; +static bool use_sni = false; +static bool verbose = false; +static bool show_extended_perfdata = false; +static bool show_body = false; +static int sd; +static int min_page_len = 0; +static int max_page_len = 0; +static int redir_depth = 0; +static int max_depth = DEFAULT_MAX_REDIRS; +static char *http_method; +static char *http_method_proxy; +static char *http_post_data; +static char *http_content_type; +static char buffer[MAX_INPUT_BUFFER]; +static char *client_cert = NULL; +static char *client_privkey = NULL; // Forward function declarations -bool process_arguments (int, char **); -int check_http (void); -void redir (char *pos, char *status_line); -bool server_type_check(const char *type); -int server_port_check(int ssl_flag); -char *perfd_time (double microsec); -char *perfd_time_connect (double microsec); -char *perfd_time_ssl (double microsec); -char *perfd_time_firstbyte (double microsec); -char *perfd_time_headers (double microsec); -char *perfd_time_transfer (double microsec); -char *perfd_size (int page_len); -void print_help (void); -void print_usage (void); -char *unchunk_content(const char *content); - -int -main (int argc, char **argv) -{ - int result = STATE_UNKNOWN; - - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); - - /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */ - server_url = strdup(HTTP_URL); - server_url_length = strlen(server_url); - xasprintf (&user_agent, "User-Agent: check_http/v%s (monitoring-plugins %s)", - NP_VERSION, VERSION); - - /* Parse extra opts if any */ - argv=np_extra_opts (&argc, argv, progname); - - if (process_arguments (argc, argv) == false) - usage4 (_("Could not parse arguments")); - - if (display_html == true) - printf ("", - use_ssl ? "https" : "http", host_name ? host_name : server_address, - server_port, server_url); - - /* initialize alarm signal handling, set socket timeout, start timer */ - (void) signal (SIGALRM, socket_timeout_alarm_handler); - (void) alarm (socket_timeout); - gettimeofday (&tv, NULL); - - result = check_http (); - return result; +static bool process_arguments(int /*argc*/, char ** /*argv*/); +static int check_http(void); +static void redir(char *pos, char *status_line); +static bool server_type_check(const char *type); +static int server_port_check(int ssl_flag); +static char *perfd_time(double elapsed_time); +static char *perfd_time_connect(double elapsed_time_connect); +static char *perfd_time_ssl(double elapsed_time_ssl); +static char *perfd_time_firstbyte(double elapsed_time_firstbyte); +static char *perfd_time_headers(double elapsed_time_headers); +static char *perfd_time_transfer(double elapsed_time_transfer); +static char *perfd_size(int page_len); +void print_help(void); +void print_usage(void); +static char *unchunk_content(const char *content); + +int main(int argc, char **argv) { + int result = STATE_UNKNOWN; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */ + server_url = strdup(HTTP_URL); + server_url_length = strlen(server_url); + xasprintf(&user_agent, "User-Agent: check_http/v%s (monitoring-plugins %s)", NP_VERSION, + VERSION); + + /* Parse extra opts if any */ + argv = np_extra_opts(&argc, argv, progname); + + if (!process_arguments(argc, argv)) { + usage4(_("Could not parse arguments")); + } + + if (display_html) { + printf("", use_ssl ? "https" : "http", + host_name ? host_name : server_address, server_port, server_url); + } + + /* initialize alarm signal handling, set socket timeout, start timer */ + (void)signal(SIGALRM, socket_timeout_alarm_handler); + (void)alarm(socket_timeout); + gettimeofday(&tv, NULL); + + result = check_http(); + return result; } /* check whether a file exists */ -void -test_file (char *path) -{ - if (access(path, R_OK) == 0) - return; - usage2 (_("file does not exist or is not readable"), path); +void test_file(char *path) { + if (access(path, R_OK) == 0) { + return; + } + usage2(_("file does not exist or is not readable"), path); } /* * process command-line arguments * returns true on success, false otherwise - */ -bool process_arguments (int argc, char **argv) -{ - int c = 1; - char *p; - char *temp; - - enum { - INVERT_REGEX = CHAR_MAX + 1, - SNI_OPTION, - MAX_REDIRS_OPTION, - CONTINUE_AFTER_CHECK_CERT, - STATE_REGEX - }; - - int option = 0; - static struct option longopts[] = { - STD_LONG_OPTS, - {"link", no_argument, 0, 'L'}, - {"nohtml", no_argument, 0, 'n'}, - {"ssl", optional_argument, 0, 'S'}, - {"sni", no_argument, 0, SNI_OPTION}, - {"post", required_argument, 0, 'P'}, - {"method", required_argument, 0, 'j'}, - {"IP-address", required_argument, 0, 'I'}, - {"url", required_argument, 0, 'u'}, - {"port", required_argument, 0, 'p'}, - {"authorization", required_argument, 0, 'a'}, - {"proxy-authorization", required_argument, 0, 'b'}, - {"header-string", required_argument, 0, 'd'}, - {"string", required_argument, 0, 's'}, - {"expect", required_argument, 0, 'e'}, - {"regex", required_argument, 0, 'r'}, - {"ereg", required_argument, 0, 'r'}, - {"eregi", required_argument, 0, 'R'}, - {"linespan", no_argument, 0, 'l'}, - {"onredirect", required_argument, 0, 'f'}, - {"certificate", required_argument, 0, 'C'}, - {"client-cert", required_argument, 0, 'J'}, - {"private-key", required_argument, 0, 'K'}, - {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, - {"useragent", required_argument, 0, 'A'}, - {"header", required_argument, 0, 'k'}, - {"no-body", no_argument, 0, 'N'}, - {"max-age", required_argument, 0, 'M'}, - {"content-type", required_argument, 0, 'T'}, - {"pagesize", required_argument, 0, 'm'}, - {"invert-regex", no_argument, NULL, INVERT_REGEX}, - {"state-regex", required_argument, 0, STATE_REGEX}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"extended-perfdata", no_argument, 0, 'E'}, - {"show-body", no_argument, 0, 'B'}, - {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, - {0, 0, 0, 0} - }; - - if (argc < 2) - return false; - - for (c = 1; c < argc; c++) { - if (strcmp ("-to", argv[c]) == 0) - strcpy (argv[c], "-t"); - if (strcmp ("-hn", argv[c]) == 0) - strcpy (argv[c], "-H"); - if (strcmp ("-wt", argv[c]) == 0) - strcpy (argv[c], "-w"); - if (strcmp ("-ct", argv[c]) == 0) - strcpy (argv[c], "-c"); - if (strcmp ("-nohtml", argv[c]) == 0) - strcpy (argv[c], "-n"); - } - - while (1) { - c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NEB", longopts, &option); - if (c == -1 || c == EOF) - break; - - switch (c) { - case '?': /* usage */ - usage5 (); - break; - case 'h': /* help */ - print_help (); - exit (STATE_UNKNOWN); - break; - case 'V': /* version */ - print_revision (progname, NP_VERSION); - exit (STATE_UNKNOWN); - break; - case 't': /* timeout period */ - if (!is_intnonneg (optarg)) - usage2 (_("Timeout interval must be a positive integer"), optarg); - else - socket_timeout = atoi (optarg); - break; - case 'c': /* critical time threshold */ - critical_thresholds = optarg; - break; - case 'w': /* warning time threshold */ - warning_thresholds = optarg; - break; - case 'A': /* User Agent String */ - xasprintf (&user_agent, "User-Agent: %s", optarg); - break; - case 'k': /* Additional headers */ - if (http_opt_headers_count == 0) - http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count)); - else - http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count)); - http_opt_headers[http_opt_headers_count - 1] = optarg; - /* xasprintf (&http_opt_headers, "%s", optarg); */ - break; - case 'L': /* show html link */ - display_html = true; - break; - case 'n': /* do not show html link */ - display_html = false; - break; - case 'C': /* Check SSL cert validity */ + */ +bool process_arguments(int argc, char **argv) { + int c = 1; + char *p; + char *temp; + + enum { + INVERT_REGEX = CHAR_MAX + 1, + SNI_OPTION, + MAX_REDIRS_OPTION, + CONTINUE_AFTER_CHECK_CERT, + STATE_REGEX + }; + + int option = 0; + static struct option longopts[] = { + STD_LONG_OPTS, + {"link", no_argument, 0, 'L'}, + {"nohtml", no_argument, 0, 'n'}, + {"ssl", optional_argument, 0, 'S'}, + {"sni", no_argument, 0, SNI_OPTION}, + {"post", required_argument, 0, 'P'}, + {"method", required_argument, 0, 'j'}, + {"IP-address", required_argument, 0, 'I'}, + {"url", required_argument, 0, 'u'}, + {"port", required_argument, 0, 'p'}, + {"authorization", required_argument, 0, 'a'}, + {"proxy-authorization", required_argument, 0, 'b'}, + {"header-string", required_argument, 0, 'd'}, + {"string", required_argument, 0, 's'}, + {"expect", required_argument, 0, 'e'}, + {"regex", required_argument, 0, 'r'}, + {"ereg", required_argument, 0, 'r'}, + {"eregi", required_argument, 0, 'R'}, + {"linespan", no_argument, 0, 'l'}, + {"onredirect", required_argument, 0, 'f'}, + {"certificate", required_argument, 0, 'C'}, + {"client-cert", required_argument, 0, 'J'}, + {"private-key", required_argument, 0, 'K'}, + {"continue-after-certificate", no_argument, 0, CONTINUE_AFTER_CHECK_CERT}, + {"useragent", required_argument, 0, 'A'}, + {"header", required_argument, 0, 'k'}, + {"no-body", no_argument, 0, 'N'}, + {"max-age", required_argument, 0, 'M'}, + {"content-type", required_argument, 0, 'T'}, + {"pagesize", required_argument, 0, 'm'}, + {"invert-regex", no_argument, NULL, INVERT_REGEX}, + {"state-regex", required_argument, 0, STATE_REGEX}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"extended-perfdata", no_argument, 0, 'E'}, + {"show-body", no_argument, 0, 'B'}, + {"max-redirs", required_argument, 0, MAX_REDIRS_OPTION}, + {0, 0, 0, 0}}; + + if (argc < 2) { + return false; + } + + for (c = 1; c < argc; c++) { + if (strcmp("-to", argv[c]) == 0) { + strcpy(argv[c], "-t"); + } + if (strcmp("-hn", argv[c]) == 0) { + strcpy(argv[c], "-H"); + } + if (strcmp("-wt", argv[c]) == 0) { + strcpy(argv[c], "-w"); + } + if (strcmp("-ct", argv[c]) == 0) { + strcpy(argv[c], "-c"); + } + if (strcmp("-nohtml", argv[c]) == 0) { + strcpy(argv[c], "-n"); + } + } + + while (1) { + c = getopt_long(argc, argv, + "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NEB", + longopts, &option); + if (c == -1 || c == EOF) { + break; + } + + switch (c) { + case '?': /* usage */ + usage5(); + break; + case 'h': /* help */ + print_help(); + exit(STATE_UNKNOWN); + break; + case 'V': /* version */ + print_revision(progname, NP_VERSION); + exit(STATE_UNKNOWN); + break; + case 't': /* timeout period */ + if (!is_intnonneg(optarg)) { + usage2(_("Timeout interval must be a positive integer"), optarg); + } else { + socket_timeout = atoi(optarg); + } + break; + case 'c': /* critical time threshold */ + critical_thresholds = optarg; + break; + case 'w': /* warning time threshold */ + warning_thresholds = optarg; + break; + case 'A': /* User Agent String */ + xasprintf(&user_agent, "User-Agent: %s", optarg); + break; + case 'k': /* Additional headers */ + if (http_opt_headers_count == 0) { + http_opt_headers = malloc(sizeof(char *) * (++http_opt_headers_count)); + } else { + http_opt_headers = + realloc(http_opt_headers, sizeof(char *) * (++http_opt_headers_count)); + } + http_opt_headers[http_opt_headers_count - 1] = optarg; + /* xasprintf (&http_opt_headers, "%s", optarg); */ + break; + case 'L': /* show html link */ + display_html = true; + break; + case 'n': /* do not show html link */ + display_html = false; + break; + case 'C': /* Check SSL cert validity */ #ifdef HAVE_SSL - if ((temp=strchr(optarg,','))!=NULL) { - *temp='\0'; - if (!is_intnonneg (optarg)) - usage2 (_("Invalid certificate expiration period"), optarg); - days_till_exp_warn = atoi(optarg); - *temp=','; - temp++; - if (!is_intnonneg (temp)) - usage2 (_("Invalid certificate expiration period"), temp); - days_till_exp_crit = atoi (temp); - } - else { - days_till_exp_crit=0; - if (!is_intnonneg (optarg)) - usage2 (_("Invalid certificate expiration period"), optarg); - days_till_exp_warn = atoi (optarg); - } - check_cert = true; - goto enable_ssl; + if ((temp = strchr(optarg, ',')) != NULL) { + *temp = '\0'; + if (!is_intnonneg(optarg)) { + usage2(_("Invalid certificate expiration period"), optarg); + } + days_till_exp_warn = atoi(optarg); + *temp = ','; + temp++; + if (!is_intnonneg(temp)) { + usage2(_("Invalid certificate expiration period"), temp); + } + days_till_exp_crit = atoi(temp); + } else { + days_till_exp_crit = 0; + if (!is_intnonneg(optarg)) { + usage2(_("Invalid certificate expiration period"), optarg); + } + days_till_exp_warn = atoi(optarg); + } + check_cert = true; + goto enable_ssl; #endif - case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ + case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ #ifdef HAVE_SSL - continue_after_check_cert = true; - break; + continue_after_check_cert = true; + break; #endif - case 'J': /* use client certificate */ + case 'J': /* use client certificate */ #ifdef HAVE_SSL - test_file(optarg); - client_cert = optarg; - goto enable_ssl; + test_file(optarg); + client_cert = optarg; + goto enable_ssl; #endif - case 'K': /* use client private key */ + case 'K': /* use client private key */ #ifdef HAVE_SSL - test_file(optarg); - client_privkey = optarg; - goto enable_ssl; + test_file(optarg); + client_privkey = optarg; + goto enable_ssl; #endif - case 'S': /* use SSL */ + case 'S': /* use SSL */ #ifdef HAVE_SSL - enable_ssl: - /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps when we include multiple - parameters, like -S and -C combinations */ - use_ssl = true; - if (c=='S' && optarg != NULL) { - int got_plus = strchr(optarg, '+') != NULL; - - if (!strncmp (optarg, "1.2", 3)) - ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2; - else if (!strncmp (optarg, "1.1", 3)) - ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1; - else if (optarg[0] == '1') - ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1; - else if (optarg[0] == '3') - ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3; - else if (optarg[0] == '2') - ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2; - else - usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)")); - } - if (specify_port == false) - server_port = HTTPS_PORT; + enable_ssl: + /* ssl_version initialized to 0 as a default. Only set if it's non-zero. This helps + when we include multiple parameters, like -S and -C combinations */ + use_ssl = true; + if (c == 'S' && optarg != NULL) { + int got_plus = strchr(optarg, '+') != NULL; + + if (!strncmp(optarg, "1.2", 3)) { + ssl_version = got_plus ? MP_TLSv1_2_OR_NEWER : MP_TLSv1_2; + } else if (!strncmp(optarg, "1.1", 3)) { + ssl_version = got_plus ? MP_TLSv1_1_OR_NEWER : MP_TLSv1_1; + } else if (optarg[0] == '1') { + ssl_version = got_plus ? MP_TLSv1_OR_NEWER : MP_TLSv1; + } else if (optarg[0] == '3') { + ssl_version = got_plus ? MP_SSLv3_OR_NEWER : MP_SSLv3; + } else if (optarg[0] == '2') { + ssl_version = got_plus ? MP_SSLv2_OR_NEWER : MP_SSLv2; + } else { + usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with " + "optional '+' suffix)")); + } + } + if (!specify_port) { + server_port = HTTPS_PORT; + } #else - /* -C -J and -K fall through to here without SSL */ - usage4 (_("Invalid option - SSL is not available")); + /* -C -J and -K fall through to here without SSL */ + usage4(_("Invalid option - SSL is not available")); #endif - break; - case SNI_OPTION: - use_sni = true; - break; - case MAX_REDIRS_OPTION: - if (!is_intnonneg (optarg)) - usage2 (_("Invalid max_redirs count"), optarg); - else { - max_depth = atoi (optarg); - } - break; - case 'f': /* onredirect */ - if (!strcmp (optarg, "stickyport")) - onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST|STICKY_PORT; - else if (!strcmp (optarg, "sticky")) - onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST; - else if (!strcmp (optarg, "follow")) - onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE; - else if (!strcmp (optarg, "unknown")) - onredirect = STATE_UNKNOWN; - else if (!strcmp (optarg, "ok")) - onredirect = STATE_OK; - else if (!strcmp (optarg, "warning")) - onredirect = STATE_WARNING; - else if (!strcmp (optarg, "critical")) - onredirect = STATE_CRITICAL; - else usage2 (_("Invalid onredirect option"), optarg); - if (verbose) - printf(_("option f:%d \n"), onredirect); - break; - /* Note: H, I, and u must be malloc'd or will fail on redirects */ - case 'H': /* Host Name (virtual host) */ - host_name = strdup (optarg); - if (host_name[0] == '[') { - if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */ - virtual_port = atoi (p + 2); - /* cut off the port */ - host_name_length = strlen (host_name) - strlen (p) - 1; - free (host_name); - host_name = strndup (optarg, host_name_length); - if (specify_port == false) - server_port = virtual_port; - } - } else if ((p = strchr (host_name, ':')) != NULL - && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */ - virtual_port = atoi (p); - /* cut off the port */ - host_name_length = strlen (host_name) - strlen (p) - 1; - free (host_name); - host_name = strndup (optarg, host_name_length); - if (specify_port == false) - server_port = virtual_port; - } - break; - case 'I': /* Server IP-address */ - server_address = strdup (optarg); - break; - case 'u': /* URL path */ - server_url = strdup (optarg); - server_url_length = strlen (server_url); - break; - case 'p': /* Server port */ - if (!is_intnonneg (optarg)) - usage2 (_("Invalid port number"), optarg); - else { - server_port = atoi (optarg); - specify_port = true; - } - break; - case 'a': /* authorization info */ - strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1); - user_auth[MAX_INPUT_BUFFER - 1] = 0; - break; - case 'b': /* proxy-authorization info */ - strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1); - proxy_auth[MAX_INPUT_BUFFER - 1] = 0; - break; - case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ - if (! http_post_data) - http_post_data = strdup (optarg); - if (! http_method) - http_method = strdup("POST"); - break; - case 'j': /* Set HTTP method */ - if (http_method) - free(http_method); - http_method = strdup (optarg); - char *tmp; - if ((tmp = strstr(http_method, ":")) != NULL) { - tmp[0] = '\0'; // set the ":" in the middle to 0 - http_method_proxy = ++tmp; // this points to the second part - } - break; - case 'd': /* string or substring */ - strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1); - header_expect[MAX_INPUT_BUFFER - 1] = 0; - break; - case 's': /* string or substring */ - strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1); - string_expect[MAX_INPUT_BUFFER - 1] = 0; - break; - case 'e': /* string or substring */ - strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1); - server_expect[MAX_INPUT_BUFFER - 1] = 0; - server_expect_yn = 1; - break; - case 'T': /* Content-type */ - xasprintf (&http_content_type, "%s", optarg); - break; - case 'l': /* linespan */ - cflags &= ~REG_NEWLINE; - break; - case 'R': /* regex */ - cflags |= REG_ICASE; + break; + case SNI_OPTION: + use_sni = true; + break; + case MAX_REDIRS_OPTION: + if (!is_intnonneg(optarg)) { + usage2(_("Invalid max_redirs count"), optarg); + } else { + max_depth = atoi(optarg); + } + break; + case 'f': /* onredirect */ + if (!strcmp(optarg, "stickyport")) { + onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST | STICKY_PORT; + } else if (!strcmp(optarg, "sticky")) { + onredirect = STATE_DEPENDENT, followsticky = STICKY_HOST; + } else if (!strcmp(optarg, "follow")) { + onredirect = STATE_DEPENDENT, followsticky = STICKY_NONE; + } else if (!strcmp(optarg, "unknown")) { + onredirect = STATE_UNKNOWN; + } else if (!strcmp(optarg, "ok")) { + onredirect = STATE_OK; + } else if (!strcmp(optarg, "warning")) { + onredirect = STATE_WARNING; + } else if (!strcmp(optarg, "critical")) { + onredirect = STATE_CRITICAL; + } else { + usage2(_("Invalid onredirect option"), optarg); + } + if (verbose) { + printf(_("option f:%d \n"), onredirect); + } + break; + /* Note: H, I, and u must be malloc'd or will fail on redirects */ + case 'H': /* Host Name (virtual host) */ + host_name = strdup(optarg); + if (host_name[0] == '[') { + if ((p = strstr(host_name, "]:")) != NULL) { /* [IPv6]:port */ + virtual_port = atoi(p + 2); + /* cut off the port */ + host_name_length = strlen(host_name) - strlen(p) - 1; + free(host_name); + host_name = strndup(optarg, host_name_length); + if (!specify_port) { + server_port = virtual_port; + } + } + } else if ((p = strchr(host_name, ':')) != NULL && + strchr(++p, ':') == NULL) { /* IPv4:port or host:port */ + virtual_port = atoi(p); + /* cut off the port */ + host_name_length = strlen(host_name) - strlen(p) - 1; + free(host_name); + host_name = strndup(optarg, host_name_length); + if (!specify_port) { + server_port = virtual_port; + } + } + break; + case 'I': /* Server IP-address */ + server_address = strdup(optarg); + break; + case 'u': /* URL path */ + server_url = strdup(optarg); + server_url_length = strlen(server_url); + break; + case 'p': /* Server port */ + if (!is_intnonneg(optarg)) { + usage2(_("Invalid port number"), optarg); + } else { + server_port = atoi(optarg); + specify_port = true; + } + break; + case 'a': /* authorization info */ + strncpy(user_auth, optarg, MAX_INPUT_BUFFER - 1); + user_auth[MAX_INPUT_BUFFER - 1] = 0; + break; + case 'b': /* proxy-authorization info */ + strncpy(proxy_auth, optarg, MAX_INPUT_BUFFER - 1); + proxy_auth[MAX_INPUT_BUFFER - 1] = 0; + break; + case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ + if (!http_post_data) { + http_post_data = strdup(optarg); + } + if (!http_method) { + http_method = strdup("POST"); + } + break; + case 'j': /* Set HTTP method */ + if (http_method) { + free(http_method); + } + http_method = strdup(optarg); + char *tmp; + if ((tmp = strstr(http_method, ":")) != NULL) { + tmp[0] = '\0'; // set the ":" in the middle to 0 + http_method_proxy = ++tmp; // this points to the second part + } + break; + case 'd': /* string or substring */ + strncpy(header_expect, optarg, MAX_INPUT_BUFFER - 1); + header_expect[MAX_INPUT_BUFFER - 1] = 0; + break; + case 's': /* string or substring */ + strncpy(string_expect, optarg, MAX_INPUT_BUFFER - 1); + string_expect[MAX_INPUT_BUFFER - 1] = 0; + break; + case 'e': /* string or substring */ + strncpy(server_expect, optarg, MAX_INPUT_BUFFER - 1); + server_expect[MAX_INPUT_BUFFER - 1] = 0; + server_expect_yn = 1; + break; + case 'T': /* Content-type */ + xasprintf(&http_content_type, "%s", optarg); + break; + case 'l': /* linespan */ + cflags &= ~REG_NEWLINE; + break; + case 'R': /* regex */ + cflags |= REG_ICASE; // fall through - case 'r': /* regex */ - strncpy (regexp, optarg, MAX_RE_SIZE - 1); - regexp[MAX_RE_SIZE - 1] = 0; - errcode = regcomp (&preg, regexp, cflags); - if (errcode != 0) { - (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); - printf (_("Could Not Compile Regular Expression: %s"), errbuf); - return false; - } - break; - case INVERT_REGEX: - invert_regex = 1; - break; - case STATE_REGEX: - if (!strcmp (optarg, "critical")) - state_regex = STATE_CRITICAL; - else if (!strcmp (optarg, "warning")) - state_regex = STATE_WARNING; - else usage2 (_("Invalid state-regex option"), optarg); - break; - case '4': - address_family = AF_INET; - break; - case '6': + case 'r': /* regex */ + strncpy(regexp, optarg, MAX_RE_SIZE - 1); + regexp[MAX_RE_SIZE - 1] = 0; + errcode = regcomp(&preg, regexp, cflags); + if (errcode != 0) { + (void)regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); + printf(_("Could Not Compile Regular Expression: %s"), errbuf); + return false; + } + break; + case INVERT_REGEX: + invert_regex = 1; + break; + case STATE_REGEX: + if (!strcmp(optarg, "critical")) { + state_regex = STATE_CRITICAL; + } else if (!strcmp(optarg, "warning")) { + state_regex = STATE_WARNING; + } else { + usage2(_("Invalid state-regex option"), optarg); + } + break; + case '4': + address_family = AF_INET; + break; + case '6': #ifdef USE_IPV6 - address_family = AF_INET6; + address_family = AF_INET6; #else - usage4 (_("IPv6 support not available")); + usage4(_("IPv6 support not available")); #endif - break; - case 'v': /* verbose */ - verbose = true; - break; - case 'm': /* min_page_length */ - { - char *tmp; - if (strchr(optarg, ':') != (char *)NULL) { - /* range, so get two values, min:max */ - tmp = strtok(optarg, ":"); - if (tmp == NULL) { - printf("Bad format: try \"-m min:max\"\n"); - exit (STATE_WARNING); - } else - min_page_len = atoi(tmp); - - tmp = strtok(NULL, ":"); - if (tmp == NULL) { - printf("Bad format: try \"-m min:max\"\n"); - exit (STATE_WARNING); - } else - max_page_len = atoi(tmp); - } else - min_page_len = atoi (optarg); - break; - } - case 'N': /* no-body */ - no_body = true; - break; - case 'M': /* max-age */ - { - int L = strlen(optarg); - if (L && optarg[L-1] == 'm') - maximum_age = atoi (optarg) * 60; - else if (L && optarg[L-1] == 'h') - maximum_age = atoi (optarg) * 60 * 60; - else if (L && optarg[L-1] == 'd') - maximum_age = atoi (optarg) * 60 * 60 * 24; - else if (L && (optarg[L-1] == 's' || - isdigit (optarg[L-1]))) - maximum_age = atoi (optarg); - else { - fprintf (stderr, "unparsable max-age: %s\n", optarg); - exit (STATE_WARNING); - } - } - break; - case 'E': /* show extended perfdata */ - show_extended_perfdata = true; - break; - case 'B': /* print body content after status line */ - show_body = true; - break; - } - } - - c = optind; - - if (server_address == NULL && c < argc) - server_address = strdup (argv[c++]); - - if (host_name == NULL && c < argc) - host_name = strdup (argv[c++]); - - if (server_address == NULL) { - if (host_name == NULL) - usage4 (_("You must specify a server address or host name")); - else - server_address = strdup (host_name); - } - - set_thresholds(&thlds, warning_thresholds, critical_thresholds); - - if (critical_thresholds && thlds->critical->end>(double)socket_timeout) - socket_timeout = (int)thlds->critical->end + 1; - - if (http_method == NULL) - http_method = strdup ("GET"); - - if (http_method_proxy == NULL) - http_method_proxy = strdup ("GET"); - - if (client_cert && !client_privkey) - usage4 (_("If you use a client certificate you must also specify a private key file")); - - if (virtual_port == 0) - virtual_port = server_port; - - return true; -} + break; + case 'v': /* verbose */ + verbose = true; + break; + case 'm': /* min_page_length */ + { + char *tmp; + if (strchr(optarg, ':') != (char *)NULL) { + /* range, so get two values, min:max */ + tmp = strtok(optarg, ":"); + if (tmp == NULL) { + printf("Bad format: try \"-m min:max\"\n"); + exit(STATE_WARNING); + } else { + min_page_len = atoi(tmp); + } + + tmp = strtok(NULL, ":"); + if (tmp == NULL) { + printf("Bad format: try \"-m min:max\"\n"); + exit(STATE_WARNING); + } else { + max_page_len = atoi(tmp); + } + } else { + min_page_len = atoi(optarg); + } + break; + } + case 'N': /* no-body */ + no_body = true; + break; + case 'M': /* max-age */ + { + int L = strlen(optarg); + if (L && optarg[L - 1] == 'm') { + maximum_age = atoi(optarg) * 60; + } else if (L && optarg[L - 1] == 'h') { + maximum_age = atoi(optarg) * 60 * 60; + } else if (L && optarg[L - 1] == 'd') { + maximum_age = atoi(optarg) * 60 * 60 * 24; + } else if (L && (optarg[L - 1] == 's' || isdigit(optarg[L - 1]))) { + maximum_age = atoi(optarg); + } else { + fprintf(stderr, "unparsable max-age: %s\n", optarg); + exit(STATE_WARNING); + } + } break; + case 'E': /* show extended perfdata */ + show_extended_perfdata = true; + break; + case 'B': /* print body content after status line */ + show_body = true; + break; + } + } + + c = optind; + + if (server_address == NULL && c < argc) { + server_address = strdup(argv[c++]); + } + if (host_name == NULL && c < argc) { + host_name = strdup(argv[c++]); + } + + if (server_address == NULL) { + if (host_name == NULL) { + usage4(_("You must specify a server address or host name")); + } else { + server_address = strdup(host_name); + } + } + set_thresholds(&thlds, warning_thresholds, critical_thresholds); + + if (critical_thresholds && thlds->critical->end > (double)socket_timeout) { + socket_timeout = (int)thlds->critical->end + 1; + } + + if (http_method == NULL) { + http_method = strdup("GET"); + } + + if (http_method_proxy == NULL) { + http_method_proxy = strdup("GET"); + } + + if (client_cert && !client_privkey) { + usage4(_("If you use a client certificate you must also specify a private key file")); + } + + if (virtual_port == 0) { + virtual_port = server_port; + } + + return true; +} /* Returns 1 if we're done processing the document body; 0 to keep going */ -static int -document_headers_done (char *full_page) -{ - const char *body; +static int document_headers_done(char *full_page) { + const char *body; - for (body = full_page; *body; body++) { - if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3)) - break; - } + for (body = full_page; *body; body++) { + if (!strncmp(body, "\n\n", 2) || !strncmp(body, "\n\r\n", 3)) { + break; + } + } - if (!*body) - return 0; /* haven't read end of headers yet */ + if (!*body) { + return 0; /* haven't read end of headers yet */ + } - full_page[body - full_page] = 0; - return 1; + full_page[body - full_page] = 0; + return 1; } -static time_t -parse_time_string (const char *string) -{ - struct tm tm; - time_t t; - memset (&tm, 0, sizeof(tm)); - - /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */ - - if (isupper (string[0]) && /* Tue */ - islower (string[1]) && - islower (string[2]) && - ',' == string[3] && - ' ' == string[4] && - (isdigit(string[5]) || string[5] == ' ') && /* 25 */ - isdigit (string[6]) && - ' ' == string[7] && - isupper (string[8]) && /* Dec */ - islower (string[9]) && - islower (string[10]) && - ' ' == string[11] && - isdigit (string[12]) && /* 2001 */ - isdigit (string[13]) && - isdigit (string[14]) && - isdigit (string[15]) && - ' ' == string[16] && - isdigit (string[17]) && /* 02: */ - isdigit (string[18]) && - ':' == string[19] && - isdigit (string[20]) && /* 59: */ - isdigit (string[21]) && - ':' == string[22] && - isdigit (string[23]) && /* 03 */ - isdigit (string[24]) && - ' ' == string[25] && - 'G' == string[26] && /* GMT */ - 'M' == string[27] && /* GMT */ - 'T' == string[28]) { - - tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0'); - tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0'); - tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0'); - tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0'); - tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 : - !strncmp (string+8, "Feb", 3) ? 1 : - !strncmp (string+8, "Mar", 3) ? 2 : - !strncmp (string+8, "Apr", 3) ? 3 : - !strncmp (string+8, "May", 3) ? 4 : - !strncmp (string+8, "Jun", 3) ? 5 : - !strncmp (string+8, "Jul", 3) ? 6 : - !strncmp (string+8, "Aug", 3) ? 7 : - !strncmp (string+8, "Sep", 3) ? 8 : - !strncmp (string+8, "Oct", 3) ? 9 : - !strncmp (string+8, "Nov", 3) ? 10 : - !strncmp (string+8, "Dec", 3) ? 11 : - -1); - tm.tm_year = ((1000 * (string[12]-'0') + - 100 * (string[13]-'0') + - 10 * (string[14]-'0') + - (string[15]-'0')) - - 1900); - - tm.tm_isdst = 0; /* GMT is never in DST, right? */ - - if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) - return 0; - - /* - This is actually wrong: we need to subtract the local timezone - offset from GMT from this value. But, that's ok in this usage, - because we only comparing these two GMT dates against each other, - so it doesn't matter what time zone we parse them in. - */ - - t = mktime (&tm); - if (t == (time_t) -1) t = 0; - - if (verbose) { - const char *s = string; - while (*s && *s != '\r' && *s != '\n') - fputc (*s++, stdout); - printf (" ==> %lu\n", (unsigned long) t); - } - - return t; - - } else { - return 0; - } +static time_t parse_time_string(const char *string) { + struct tm tm; + time_t t; + memset(&tm, 0, sizeof(tm)); + + /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */ + + if (isupper(string[0]) && /* Tue */ + islower(string[1]) && islower(string[2]) && ',' == string[3] && ' ' == string[4] && + (isdigit(string[5]) || string[5] == ' ') && /* 25 */ + isdigit(string[6]) && ' ' == string[7] && isupper(string[8]) && /* Dec */ + islower(string[9]) && islower(string[10]) && ' ' == string[11] && + isdigit(string[12]) && /* 2001 */ + isdigit(string[13]) && isdigit(string[14]) && isdigit(string[15]) && ' ' == string[16] && + isdigit(string[17]) && /* 02: */ + isdigit(string[18]) && ':' == string[19] && isdigit(string[20]) && /* 59: */ + isdigit(string[21]) && ':' == string[22] && isdigit(string[23]) && /* 03 */ + isdigit(string[24]) && ' ' == string[25] && 'G' == string[26] && /* GMT */ + 'M' == string[27] && /* GMT */ + 'T' == string[28]) { + + tm.tm_sec = 10 * (string[23] - '0') + (string[24] - '0'); + tm.tm_min = 10 * (string[20] - '0') + (string[21] - '0'); + tm.tm_hour = 10 * (string[17] - '0') + (string[18] - '0'); + tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5] - '0') + (string[6] - '0'); + tm.tm_mon = (!strncmp(string + 8, "Jan", 3) ? 0 + : !strncmp(string + 8, "Feb", 3) ? 1 + : !strncmp(string + 8, "Mar", 3) ? 2 + : !strncmp(string + 8, "Apr", 3) ? 3 + : !strncmp(string + 8, "May", 3) ? 4 + : !strncmp(string + 8, "Jun", 3) ? 5 + : !strncmp(string + 8, "Jul", 3) ? 6 + : !strncmp(string + 8, "Aug", 3) ? 7 + : !strncmp(string + 8, "Sep", 3) ? 8 + : !strncmp(string + 8, "Oct", 3) ? 9 + : !strncmp(string + 8, "Nov", 3) ? 10 + : !strncmp(string + 8, "Dec", 3) ? 11 + : -1); + tm.tm_year = ((1000 * (string[12] - '0') + 100 * (string[13] - '0') + + 10 * (string[14] - '0') + (string[15] - '0')) - + 1900); + + tm.tm_isdst = 0; /* GMT is never in DST, right? */ + + if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) { + return 0; + } + + /* + This is actually wrong: we need to subtract the local timezone + offset from GMT from this value. But, that's ok in this usage, + because we only comparing these two GMT dates against each other, + so it doesn't matter what time zone we parse them in. + */ + + t = mktime(&tm); + if (t == (time_t)-1) { + t = 0; + } + + if (verbose) { + const char *s = string; + while (*s && *s != '\r' && *s != '\n') { + fputc(*s++, stdout); + } + printf(" ==> %lu\n", (unsigned long)t); + } + + return t; + } + return 0; } /* Checks if the server 'reply' is one of the expected 'statuscodes' */ -static int -expected_statuscode (const char *reply, const char *statuscodes) -{ - char *expected, *code; - int result = 0; - - if ((expected = strdup (statuscodes)) == NULL) - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); - - for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ",")) - if (strstr (reply, code) != NULL) { - result = 1; - break; - } - - free (expected); - return result; +static int expected_statuscode(const char *reply, const char *statuscodes) { + char *expected; + char *code; + int result = 0; + + if ((expected = strdup(statuscodes)) == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } + + for (code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { + if (strstr(reply, code) != NULL) { + result = 1; + break; + } + } + + free(expected); + return result; } -static int -check_document_dates (const char *headers, char **msg) -{ - const char *s; - char *server_date = 0; - char *document_date = 0; - int date_result = STATE_OK; - - s = headers; - while (*s) { - const char *field = s; - const char *value = 0; - - /* Find the end of the header field */ - while (*s && !isspace(*s) && *s != ':') - s++; - - /* Remember the header value, if any. */ - if (*s == ':') - value = ++s; - - /* Skip to the end of the header, including continuation lines. */ - while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) - s++; - - /* Avoid stepping over end-of-string marker */ - if (*s) - s++; - - /* Process this header. */ - if (value && value > field+2) { - char *ff = (char *) malloc (value-field); - char *ss = ff; - while (field < value-1) - *ss++ = tolower(*field++); - *ss++ = 0; - - if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) { - const char *e; - while (*value && isspace (*value)) - value++; - for (e = value; *e && *e != '\r' && *e != '\n'; e++) - ; - ss = (char *) malloc (e - value + 1); - strncpy (ss, value, e - value); - ss[e - value] = 0; - if (!strcmp (ff, "date")) { - if (server_date) free (server_date); - server_date = ss; - } else { - if (document_date) free (document_date); - document_date = ss; - } - } - free (ff); - } - } - - /* Done parsing the body. Now check the dates we (hopefully) parsed. */ - if (!server_date || !*server_date) { - xasprintf (msg, _("%sServer date unknown, "), *msg); - date_result = max_state_alt(STATE_UNKNOWN, date_result); - } else if (!document_date || !*document_date) { - xasprintf (msg, _("%sDocument modification date unknown, "), *msg); - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else { - time_t srv_data = parse_time_string (server_date); - time_t doc_data = parse_time_string (document_date); - - if (srv_data <= 0) { - xasprintf (msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data <= 0) { - xasprintf (msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data > srv_data + 30) { - xasprintf (msg, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data < srv_data - maximum_age) { - int n = (srv_data - doc_data); - if (n > (60 * 60 * 24 * 2)) { - xasprintf (msg, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24)); - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else { - xasprintf (msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); - date_result = max_state_alt(STATE_CRITICAL, date_result); - } - } - free (server_date); - free (document_date); - } - return date_result; +static int check_document_dates(const char *headers, char **msg) { + const char *s; + char *server_date = 0; + char *document_date = 0; + int date_result = STATE_OK; + + s = headers; + while (*s) { + const char *field = s; + const char *value = 0; + + /* Find the end of the header field */ + while (*s && !isspace(*s) && *s != ':') { + s++; + } + + /* Remember the header value, if any. */ + if (*s == ':') { + value = ++s; + } + + /* Skip to the end of the header, including continuation lines. */ + while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) { + s++; + } + + /* Avoid stepping over end-of-string marker */ + if (*s) { + s++; + } + + /* Process this header. */ + if (value && value > field + 2) { + char *ff = (char *)malloc(value - field); + char *ss = ff; + while (field < value - 1) { + *ss++ = tolower(*field++); + } + *ss++ = 0; + + if (!strcmp(ff, "date") || !strcmp(ff, "last-modified")) { + const char *e; + while (*value && isspace(*value)) { + value++; + } + for (e = value; *e && *e != '\r' && *e != '\n'; e++) { + ; + } + ss = (char *)malloc(e - value + 1); + strncpy(ss, value, e - value); + ss[e - value] = 0; + if (!strcmp(ff, "date")) { + if (server_date) { + free(server_date); + } + server_date = ss; + } else { + if (document_date) { + free(document_date); + } + document_date = ss; + } + } + free(ff); + } + } + + /* Done parsing the body. Now check the dates we (hopefully) parsed. */ + if (!server_date || !*server_date) { + xasprintf(msg, _("%sServer date unknown, "), *msg); + date_result = max_state_alt(STATE_UNKNOWN, date_result); + } else if (!document_date || !*document_date) { + xasprintf(msg, _("%sDocument modification date unknown, "), *msg); + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else { + time_t srv_data = parse_time_string(server_date); + time_t doc_data = parse_time_string(document_date); + + if (srv_data <= 0) { + xasprintf(msg, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data <= 0) { + xasprintf(msg, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data > srv_data + 30) { + xasprintf(msg, _("%sDocument is %d seconds in the future, "), *msg, + (int)doc_data - (int)srv_data); + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data < srv_data - maximum_age) { + int n = (srv_data - doc_data); + if (n > (60 * 60 * 24 * 2)) { + xasprintf(msg, _("%sLast modified %.1f days ago, "), *msg, + ((float)n) / (60 * 60 * 24)); + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else { + xasprintf(msg, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), + (n / 60) % 60, n % 60); + date_result = max_state_alt(STATE_CRITICAL, date_result); + } + } + free(server_date); + free(document_date); + } + return date_result; } -int -get_content_length (const char *headers) -{ - const char *s; - int content_length = 0; - - s = headers; - while (*s) { - const char *field = s; - const char *value = 0; - - /* Find the end of the header field */ - while (*s && !isspace(*s) && *s != ':') - s++; - - /* Remember the header value, if any. */ - if (*s == ':') - value = ++s; - - /* Skip to the end of the header, including continuation lines. */ - while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) - s++; - - /* Avoid stepping over end-of-string marker */ - if (*s) - s++; - - /* Process this header. */ - if (value && value > field+2) { - char *ff = (char *) malloc (value-field); - char *ss = ff; - while (field < value-1) - *ss++ = tolower(*field++); - *ss++ = 0; - - if (!strcmp (ff, "content-length")) { - const char *e; - while (*value && isspace (*value)) - value++; - for (e = value; *e && *e != '\r' && *e != '\n'; e++) - ; - ss = (char *) malloc (e - value + 1); - strncpy (ss, value, e - value); - ss[e - value] = 0; - content_length = atoi(ss); - free (ss); - } - free (ff); - } - } - return (content_length); +int get_content_length(const char *headers) { + const char *s; + int content_length = 0; + + s = headers; + while (*s) { + const char *field = s; + const char *value = 0; + + /* Find the end of the header field */ + while (*s && !isspace(*s) && *s != ':') { + s++; + } + + /* Remember the header value, if any. */ + if (*s == ':') { + value = ++s; + } + + /* Skip to the end of the header, including continuation lines. */ + while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t'))) { + s++; + } + + /* Avoid stepping over end-of-string marker */ + if (*s) { + s++; + } + + /* Process this header. */ + if (value && value > field + 2) { + char *ff = (char *)malloc(value - field); + char *ss = ff; + while (field < value - 1) { + *ss++ = tolower(*field++); + } + *ss++ = 0; + + if (!strcmp(ff, "content-length")) { + const char *e; + while (*value && isspace(*value)) { + value++; + } + for (e = value; *e && *e != '\r' && *e != '\n'; e++) { + ; + } + ss = (char *)malloc(e - value + 1); + strncpy(ss, value, e - value); + ss[e - value] = 0; + content_length = atoi(ss); + free(ss); + } + free(ff); + } + } + return (content_length); } -char * -prepend_slash (char *path) -{ - char *newpath; +char *prepend_slash(char *path) { + char *newpath; - if (path[0] == '/') - return path; + if (path[0] == '/') { + return path; + } - if ((newpath = malloc (strlen(path) + 2)) == NULL) - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); - newpath[0] = '/'; - strcpy (newpath + 1, path); - free (path); - return newpath; + if ((newpath = malloc(strlen(path) + 2)) == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } + newpath[0] = '/'; + strcpy(newpath + 1, path); + free(path); + return newpath; } -int -check_http (void) -{ - char *msg; - char *status_line; - char *status_code; - char *header; - char *page; - char *auth; - int http_status; - int i = 0; - size_t pagesize = 0; - char *full_page; - char *full_page_new; - char *buf; - char *pos; - long microsec = 0L; - double elapsed_time = 0.0; - long microsec_connect = 0L; - double elapsed_time_connect = 0.0; - long microsec_ssl = 0L; - double elapsed_time_ssl = 0.0; - long microsec_firstbyte = 0L; - double elapsed_time_firstbyte = 0.0; - long microsec_headers = 0L; - double elapsed_time_headers = 0.0; - long microsec_transfer = 0L; - double elapsed_time_transfer = 0.0; - int page_len = 0; - int result = STATE_OK; - char *force_host_header = NULL; - - /* try to connect to the host at the given port number */ - gettimeofday (&tv_temp, NULL); - if (my_tcp_connect (server_address, server_port, &sd) != STATE_OK) - die (STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n")); - microsec_connect = deltime (tv_temp); - - /* if we are called with the -I option, the -j method is CONNECT and */ - /* we received -S for SSL, then we tunnel the request through a proxy*/ - /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */ - - if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 - && host_name != NULL && use_ssl == true) { - - if (verbose) printf ("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, server_port, host_name, HTTPS_PORT); - asprintf (&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, user_agent); - if (strlen(proxy_auth)) { - base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); - xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); - } - /* optionally send any other header tag */ - if (http_opt_headers_count) { - for (i = 0; i < http_opt_headers_count ; i++) { - if (force_host_header != http_opt_headers[i]) { - xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); - } - } - /* This cannot be free'd here because a redirection will then try to access this and segfault */ - /* Covered in a testcase in tests/check_http.t */ - /* free(http_opt_headers); */ - } - asprintf (&buf, "%sProxy-Connection: keep-alive\r\n", buf); - asprintf (&buf, "%sHost: %s\r\n", buf, host_name); - /* we finished our request, send empty line with CRLF */ - asprintf (&buf, "%s%s", buf, CRLF); - if (verbose) printf ("%s\n", buf); - send(sd, buf, strlen (buf), 0); - buf[0]='\0'; - - if (verbose) printf ("Receive response from proxy\n"); - read (sd, buffer, MAX_INPUT_BUFFER-1); - if (verbose) printf ("%s", buffer); - /* Here we should check if we got HTTP/1.1 200 Connection established */ - } +int check_http(void) { + char *msg; + char *status_line; + char *status_code; + char *header; + char *page; + char *auth; + int http_status; + int i = 0; + size_t pagesize = 0; + char *full_page; + char *full_page_new; + char *buf; + char *pos; + long microsec = 0L; + double elapsed_time = 0.0; + long microsec_connect = 0L; + double elapsed_time_connect = 0.0; + long microsec_ssl = 0L; + double elapsed_time_ssl = 0.0; + long microsec_firstbyte = 0L; + double elapsed_time_firstbyte = 0.0; + long microsec_headers = 0L; + double elapsed_time_headers = 0.0; + long microsec_transfer = 0L; + double elapsed_time_transfer = 0.0; + int page_len = 0; + int result = STATE_OK; + char *force_host_header = NULL; + + /* try to connect to the host at the given port number */ + gettimeofday(&tv_temp, NULL); + if (my_tcp_connect(server_address, server_port, &sd) != STATE_OK) { + die(STATE_CRITICAL, _("HTTP CRITICAL - Unable to open TCP socket\n")); + } + microsec_connect = deltime(tv_temp); + + /* if we are called with the -I option, the -j method is CONNECT and */ + /* we received -S for SSL, then we tunnel the request through a proxy*/ + /* @20100414, public[at]frank4dd.com, http://www.frank4dd.com/howto */ + + if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL && + use_ssl) { + + if (verbose) { + printf("Entering CONNECT tunnel mode with proxy %s:%d to dst %s:%d\n", server_address, + server_port, host_name, HTTPS_PORT); + } + asprintf(&buf, "%s %s:%d HTTP/1.1\r\n%s\r\n", http_method, host_name, HTTPS_PORT, + user_agent); + if (strlen(proxy_auth)) { + base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth); + xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); + } + /* optionally send any other header tag */ + if (http_opt_headers_count) { + for (i = 0; i < http_opt_headers_count; i++) { + if (force_host_header != http_opt_headers[i]) { + xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]); + } + } + /* This cannot be free'd here because a redirection will then try to access this and + * segfault */ + /* Covered in a testcase in tests/check_http.t */ + /* free(http_opt_headers); */ + } + asprintf(&buf, "%sProxy-Connection: keep-alive\r\n", buf); + asprintf(&buf, "%sHost: %s\r\n", buf, host_name); + /* we finished our request, send empty line with CRLF */ + asprintf(&buf, "%s%s", buf, CRLF); + if (verbose) { + printf("%s\n", buf); + } + send(sd, buf, strlen(buf), 0); + buf[0] = '\0'; + + if (verbose) { + printf("Receive response from proxy\n"); + } + read(sd, buffer, MAX_INPUT_BUFFER - 1); + if (verbose) { + printf("%s", buffer); + } + /* Here we should check if we got HTTP/1.1 200 Connection established */ + } #ifdef HAVE_SSL - elapsed_time_connect = (double)microsec_connect / 1.0e6; - if (use_ssl == true) { - gettimeofday (&tv_temp, NULL); - result = np_net_ssl_init_with_hostname_version_and_cert(sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey); - if (verbose) printf ("SSL initialized\n"); - if (result != STATE_OK) - die (STATE_CRITICAL, NULL); - microsec_ssl = deltime (tv_temp); - elapsed_time_ssl = (double)microsec_ssl / 1.0e6; - if (check_cert == true) { - result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); - if (continue_after_check_cert == false) { - if (sd) close(sd); - np_net_ssl_cleanup(); - return result; - } - } - } + elapsed_time_connect = (double)microsec_connect / 1.0e6; + if (use_ssl) { + gettimeofday(&tv_temp, NULL); + result = np_net_ssl_init_with_hostname_version_and_cert( + sd, (use_sni ? host_name : NULL), ssl_version, client_cert, client_privkey); + if (verbose) { + printf("SSL initialized\n"); + } + if (result != STATE_OK) { + die(STATE_CRITICAL, NULL); + } + microsec_ssl = deltime(tv_temp); + elapsed_time_ssl = (double)microsec_ssl / 1.0e6; + if (check_cert) { + result = np_net_ssl_check_cert(days_till_exp_warn, days_till_exp_crit); + if (!continue_after_check_cert) { + if (sd) { + close(sd); + } + np_net_ssl_cleanup(); + return result; + } + } + } #endif /* HAVE_SSL */ - if ( server_address != NULL && strcmp(http_method, "CONNECT") == 0 - && host_name != NULL && use_ssl == true) - asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); - else - asprintf (&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); - - /* tell HTTP/1.1 servers not to keep the connection alive */ - xasprintf (&buf, "%sConnection: close\r\n", buf); - - /* check if Host header is explicitly set in options */ - if (http_opt_headers_count) { - for (i = 0; i < http_opt_headers_count ; i++) { - if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { - force_host_header = http_opt_headers[i]; - } - } - } - - /* optionally send the host header info */ - if (host_name) { - if (force_host_header) { - xasprintf (&buf, "%s%s\r\n", buf, force_host_header); - } - else { - /* - * Specify the port only if we're using a non-default port (see RFC 2616, - * 14.23). Some server applications/configurations cause trouble if the - * (default) port is explicitly specified in the "Host:" header line. - */ - if ((use_ssl == false && virtual_port == HTTP_PORT) || - (use_ssl == true && virtual_port == HTTPS_PORT) || - (server_address != NULL && strcmp(http_method, "CONNECT") == 0 - && host_name != NULL && use_ssl == true)) - xasprintf (&buf, "%sHost: %s\r\n", buf, host_name); - else - xasprintf (&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port); - } - } - - /* optionally send any other header tag */ - if (http_opt_headers_count) { - for (i = 0; i < http_opt_headers_count ; i++) { - if (force_host_header != http_opt_headers[i]) { - xasprintf (&buf, "%s%s\r\n", buf, http_opt_headers[i]); - } - } - /* This cannot be free'd here because a redirection will then try to access this and segfault */ - /* Covered in a testcase in tests/check_http.t */ - /* free(http_opt_headers); */ - } - - /* optionally send the authentication info */ - if (strlen(user_auth)) { - base64_encode_alloc (user_auth, strlen (user_auth), &auth); - xasprintf (&buf, "%sAuthorization: Basic %s\r\n", buf, auth); - } - - /* optionally send the proxy authentication info */ - if (strlen(proxy_auth)) { - base64_encode_alloc (proxy_auth, strlen (proxy_auth), &auth); - xasprintf (&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); - } - - /* either send http POST data (any data, not only POST)*/ - if (http_post_data) { - if (http_content_type) { - xasprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type); - } else { - xasprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf); - } - - xasprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen (http_post_data)); - xasprintf (&buf, "%s%s", buf, http_post_data); - } else { - /* or just a newline so the server knows we're done with the request */ - xasprintf (&buf, "%s%s", buf, CRLF); - } - - if (verbose) printf ("%s\n", buf); - gettimeofday (&tv_temp, NULL); - my_send (buf, strlen (buf)); - microsec_headers = deltime (tv_temp); - elapsed_time_headers = (double)microsec_headers / 1.0e6; - - /* fetch the page */ - full_page = strdup(""); - gettimeofday (&tv_temp, NULL); - while ((i = my_recv (buffer, MAX_INPUT_BUFFER-1)) > 0) { - if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) { - microsec_firstbyte = deltime (tv_temp); - elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6; - } - while ((pos = memchr(buffer, '\0', i))) { - /* replace nul character with a blank */ - *pos = ' '; - } - buffer[i] = '\0'; - - if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n")); - - memmove(&full_page_new[pagesize], buffer, i + 1); - - full_page = full_page_new; - - pagesize += i; - - if (no_body && document_headers_done (full_page)) { - i = 0; - break; - } - } - microsec_transfer = deltime (tv_temp); - elapsed_time_transfer = (double)microsec_transfer / 1.0e6; - - if (i < 0 && errno != ECONNRESET) { - die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n")); - } - - /* return a CRITICAL status if we couldn't read any data */ - if (pagesize == (size_t) 0) - die (STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n")); - - /* close the connection */ - if (sd) close(sd); + if (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && host_name != NULL && + use_ssl) { + asprintf(&buf, "%s %s %s\r\n%s\r\n", http_method_proxy, server_url, + host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); + } else { + asprintf(&buf, "%s %s %s\r\n%s\r\n", http_method, server_url, + host_name ? "HTTP/1.1" : "HTTP/1.0", user_agent); + } + + /* tell HTTP/1.1 servers not to keep the connection alive */ + xasprintf(&buf, "%sConnection: close\r\n", buf); + + /* check if Host header is explicitly set in options */ + if (http_opt_headers_count) { + for (i = 0; i < http_opt_headers_count; i++) { + if (strncmp(http_opt_headers[i], "Host:", 5) == 0) { + force_host_header = http_opt_headers[i]; + } + } + } + + /* optionally send the host header info */ + if (host_name) { + if (force_host_header) { + xasprintf(&buf, "%s%s\r\n", buf, force_host_header); + } else { + /* + * Specify the port only if we're using a non-default port (see RFC 2616, + * 14.23). Some server applications/configurations cause trouble if the + * (default) port is explicitly specified in the "Host:" header line. + */ + if ((!use_ssl && virtual_port == HTTP_PORT) || + (use_ssl && virtual_port == HTTPS_PORT) || + (server_address != NULL && strcmp(http_method, "CONNECT") == 0 && + host_name != NULL && use_ssl)) { + xasprintf(&buf, "%sHost: %s\r\n", buf, host_name); + } else { + xasprintf(&buf, "%sHost: %s:%d\r\n", buf, host_name, virtual_port); + } + } + } + + /* optionally send any other header tag */ + if (http_opt_headers_count) { + for (i = 0; i < http_opt_headers_count; i++) { + if (force_host_header != http_opt_headers[i]) { + xasprintf(&buf, "%s%s\r\n", buf, http_opt_headers[i]); + } + } + /* This cannot be free'd here because a redirection will then try to access this and + * segfault */ + /* Covered in a testcase in tests/check_http.t */ + /* free(http_opt_headers); */ + } + + /* optionally send the authentication info */ + if (strlen(user_auth)) { + base64_encode_alloc(user_auth, strlen(user_auth), &auth); + xasprintf(&buf, "%sAuthorization: Basic %s\r\n", buf, auth); + } + + /* optionally send the proxy authentication info */ + if (strlen(proxy_auth)) { + base64_encode_alloc(proxy_auth, strlen(proxy_auth), &auth); + xasprintf(&buf, "%sProxy-Authorization: Basic %s\r\n", buf, auth); + } + + /* either send http POST data (any data, not only POST)*/ + if (http_post_data) { + if (http_content_type) { + xasprintf(&buf, "%sContent-Type: %s\r\n", buf, http_content_type); + } else { + xasprintf(&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf); + } + + xasprintf(&buf, "%sContent-Length: %i\r\n\r\n", buf, (int)strlen(http_post_data)); + xasprintf(&buf, "%s%s", buf, http_post_data); + } else { + /* or just a newline so the server knows we're done with the request */ + xasprintf(&buf, "%s%s", buf, CRLF); + } + + if (verbose) { + printf("%s\n", buf); + } + gettimeofday(&tv_temp, NULL); + my_send(buf, strlen(buf)); + microsec_headers = deltime(tv_temp); + elapsed_time_headers = (double)microsec_headers / 1.0e6; + + /* fetch the page */ + full_page = strdup(""); + gettimeofday(&tv_temp, NULL); + while ((i = my_recv(buffer, MAX_INPUT_BUFFER - 1)) > 0) { + if ((i >= 1) && (elapsed_time_firstbyte <= 0.000001)) { + microsec_firstbyte = deltime(tv_temp); + elapsed_time_firstbyte = (double)microsec_firstbyte / 1.0e6; + } + while ((pos = memchr(buffer, '\0', i))) { + /* replace nul character with a blank */ + *pos = ' '; + } + buffer[i] = '\0'; + + if ((full_page_new = realloc(full_page, pagesize + i + 1)) == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate memory for full_page\n")); + } + + memmove(&full_page_new[pagesize], buffer, i + 1); + + full_page = full_page_new; + + pagesize += i; + + if (no_body && document_headers_done(full_page)) { + i = 0; + break; + } + } + microsec_transfer = deltime(tv_temp); + elapsed_time_transfer = (double)microsec_transfer / 1.0e6; + + if (i < 0 && errno != ECONNRESET) { + die(STATE_CRITICAL, _("HTTP CRITICAL - Error on receive\n")); + } + + /* return a CRITICAL status if we couldn't read any data */ + if (pagesize == (size_t)0) { + die(STATE_CRITICAL, _("HTTP CRITICAL - No data received from host\n")); + } + + /* close the connection */ + if (sd) { + close(sd); + } #ifdef HAVE_SSL - np_net_ssl_cleanup(); + np_net_ssl_cleanup(); #endif - /* Save check time */ - microsec = deltime (tv); - elapsed_time = (double)microsec / 1.0e6; - - /* leave full_page untouched so we can free it later */ - page = full_page; - - if (verbose) - printf ("%s://%s:%d%s is %d characters\n", - use_ssl ? "https" : "http", server_address, - server_port, server_url, (int)pagesize); - - /* find status line and null-terminate it */ - status_line = page; - page += (size_t) strcspn (page, "\r\n"); - pos = page; - page += (size_t) strspn (page, "\r\n"); - status_line[strcspn(status_line, "\r\n")] = 0; - strip (status_line); - if (verbose) - printf ("STATUS: %s\n", status_line); - - /* find header info and null-terminate it */ - header = page; - while (strcspn (page, "\r\n") > 0) { - page += (size_t) strcspn (page, "\r\n"); - pos = page; - if ((strspn (page, "\r") == 1 && strspn (page, "\r\n") >= 2) || - (strspn (page, "\n") == 1 && strspn (page, "\r\n") >= 2)) - page += (size_t) 2; - else - page += (size_t) 1; - } - page += (size_t) strspn (page, "\r\n"); - header[pos - header] = 0; - if (verbose) - printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, - (no_body ? " [[ skipped ]]" : page)); - - /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode (status_line, server_expect)) { - if (server_port == HTTP_PORT) - xasprintf (&msg, - _("Invalid HTTP response received from host: %s\n"), - status_line); - else - xasprintf (&msg, - _("Invalid HTTP response received from host on port %d: %s\n"), - server_port, status_line); - if (show_body) - xasprintf (&msg, _("%s\n%s"), msg, page); - die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg); - } - - /* Bypass normal status line check if server_expect was set by user and not default */ - /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */ - if ( server_expect_yn ) { - xasprintf (&msg, - _("Status line output matched \"%s\" - "), server_expect); - if (verbose) - printf ("%s\n",msg); - } - else { - /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ - /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */ - /* Status-Code = 3 DIGITS */ - - status_code = strchr (status_line, ' ') + sizeof (char); - if (strspn (status_code, "1234567890") != 3) - die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line); - - http_status = atoi (status_code); - - /* check the return code */ - - if (http_status >= 600 || http_status < 100) { - die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line); - } - /* server errors result in a critical state */ - else if (http_status >= 500) { - xasprintf (&msg, _("%s - "), status_line); - result = STATE_CRITICAL; - } - /* client errors result in a warning state */ - else if (http_status >= 400) { - xasprintf (&msg, _("%s - "), status_line); - result = max_state_alt(STATE_WARNING, result); - } - /* check redirected page if specified */ - else if (http_status >= 300) { - - if (onredirect == STATE_DEPENDENT) - redir (header, status_line); - else - result = max_state_alt(onredirect, result); - xasprintf (&msg, _("%s - "), status_line); - } /* end if (http_status >= 300) */ - else { - /* Print OK status anyway */ - xasprintf (&msg, _("%s - "), status_line); - } - - } /* end else (server_expect_yn) */ - - /* reset the alarm - must be called *after* redir or we'll never die on redirects! */ - alarm (0); - - if (maximum_age >= 0) { - result = max_state_alt(check_document_dates(header, &msg), result); - } - - /* Page and Header content checks go here */ - if (strlen(header_expect) > 0) { - if (strstr(header, header_expect) == NULL) { - // We did not find the header, the rest is for building the output and setting the state - char output_header_search[30] = ""; - - strncpy(&output_header_search[0], header_expect, - sizeof(output_header_search)); - - if (output_header_search[sizeof(output_header_search) - 1] != '\0') { - bcopy("...", - &output_header_search[sizeof(output_header_search) - 4], - 4); - } - - xasprintf (&msg, - _("%sheader '%s' not found on '%s://%s:%d%s', "), - msg, - output_header_search, use_ssl ? "https" : "http", - host_name ? host_name : server_address, server_port, - server_url); - - result = STATE_CRITICAL; - } - } - - // At this point we should test if the content is chunked and unchunk it, so - // it can be searched (and possibly printed) - const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *"; - regex_t chunked_header_regex; - - if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) { - die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to compile chunked_header_regex regex"); - } - - regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF it was found - - if (!no_body && regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) { - if (verbose) { - printf("Found chunked content\n"); - } - // We actually found the chunked header - char *tmp = unchunk_content(page); - if (tmp == NULL) { - die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to unchunk message body"); - } - page = tmp; - } - - if (strlen(string_expect) > 0) { - if (!strstr(page, string_expect)) { - // We found the string the body, the rest is for building the output - char output_string_search[30] = ""; - strncpy(&output_string_search[0], string_expect, - sizeof(output_string_search)); - if (output_string_search[sizeof(output_string_search) - 1] != '\0') { - bcopy("...", &output_string_search[sizeof(output_string_search) - 4], - 4); - } - xasprintf (&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url); - result = STATE_CRITICAL; - } - } - - if (strlen(regexp) > 0) { - errcode = regexec(&preg, page, REGS, pmatch, 0); - if ((errcode == 0 && invert_regex == 0) || - (errcode == REG_NOMATCH && invert_regex == 1)) { - /* OK - No-op to avoid changing the logic around it */ - result = max_state_alt(STATE_OK, result); - } - else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) { - if (invert_regex == 0) - xasprintf (&msg, _("%spattern not found, "), msg); - else - xasprintf (&msg, _("%spattern found, "), msg); - result = state_regex; - } - else { - /* FIXME: Shouldn't that be UNKNOWN? */ - regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER); - xasprintf (&msg, _("%sExecute Error: %s, "), msg, errbuf); - result = STATE_CRITICAL; - } - } - - /* make sure the page is of an appropriate size */ - /* page_len = get_content_length(header); */ - /* FIXME: Will this work with -N ? IMHO we should use - * get_content_length(header) and always check if it's different than the - * returned pagesize - */ - /* FIXME: IIRC pagesize returns headers - shouldn't we make - * it == get_content_length(header) ?? - */ - page_len = pagesize; - if ((max_page_len > 0) && (page_len > max_page_len)) { - xasprintf (&msg, _("%spage size %d too large, "), msg, page_len); - result = max_state_alt(STATE_WARNING, result); - } else if ((min_page_len > 0) && (page_len < min_page_len)) { - xasprintf (&msg, _("%spage size %d too small, "), msg, page_len); - result = max_state_alt(STATE_WARNING, result); - } - - /* Cut-off trailing characters */ - if(msg[strlen(msg)-2] == ',') - msg[strlen(msg)-2] = '\0'; - else - msg[strlen(msg)-3] = '\0'; - - /* check elapsed time */ - if (show_extended_perfdata) - xasprintf (&msg, - _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), - msg, page_len, elapsed_time, - (display_html ? "" : ""), - perfd_time (elapsed_time), - perfd_size (page_len), - perfd_time_connect (elapsed_time_connect), - use_ssl == true ? perfd_time_ssl (elapsed_time_ssl) : "", - perfd_time_headers (elapsed_time_headers), - perfd_time_firstbyte (elapsed_time_firstbyte), - perfd_time_transfer (elapsed_time_transfer)); - else - xasprintf (&msg, - _("%s - %d bytes in %.3f second response time %s|%s %s"), - msg, page_len, elapsed_time, - (display_html ? "" : ""), - perfd_time (elapsed_time), - perfd_size (page_len)); - - if (show_body) - xasprintf (&msg, _("%s\n%s"), msg, page); - - result = max_state_alt(get_status(elapsed_time, thlds), result); - - die (result, "HTTP %s: %s\n", state_text(result), msg); - /* die failed? */ - return STATE_UNKNOWN; + /* Save check time */ + microsec = deltime(tv); + elapsed_time = (double)microsec / 1.0e6; + + /* leave full_page untouched so we can free it later */ + page = full_page; + + if (verbose) { + printf("%s://%s:%d%s is %d characters\n", use_ssl ? "https" : "http", server_address, + server_port, server_url, (int)pagesize); + } + + /* find status line and null-terminate it */ + status_line = page; + page += (size_t)strcspn(page, "\r\n"); + pos = page; + page += (size_t)strspn(page, "\r\n"); + status_line[strcspn(status_line, "\r\n")] = 0; + strip(status_line); + if (verbose) { + printf("STATUS: %s\n", status_line); + } + + /* find header info and null-terminate it */ + header = page; + while (strcspn(page, "\r\n") > 0) { + page += (size_t)strcspn(page, "\r\n"); + pos = page; + if ((strspn(page, "\r") == 1 && strspn(page, "\r\n") >= 2) || + (strspn(page, "\n") == 1 && strspn(page, "\r\n") >= 2)) { + page += (size_t)2; + } else { + page += (size_t)1; + } + } + page += (size_t)strspn(page, "\r\n"); + header[pos - header] = 0; + if (verbose) { + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, + (no_body ? " [[ skipped ]]" : page)); + } + + /* make sure the status line matches the response we are looking for */ + if (!expected_statuscode(status_line, server_expect)) { + if (server_port == HTTP_PORT) { + xasprintf(&msg, _("Invalid HTTP response received from host: %s\n"), status_line); + } else { + xasprintf(&msg, _("Invalid HTTP response received from host on port %d: %s\n"), + server_port, status_line); + } + if (show_body) { + xasprintf(&msg, _("%s\n%s"), msg, page); + } + die(STATE_CRITICAL, "HTTP CRITICAL - %s", msg); + } + + /* Bypass normal status line check if server_expect was set by user and not default */ + /* NOTE: After this if/else block msg *MUST* be an asprintf-allocated string */ + if (server_expect_yn) { + xasprintf(&msg, _("Status line output matched \"%s\" - "), server_expect); + if (verbose) { + printf("%s\n", msg); + } + } else { + /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ + /* HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT */ + /* Status-Code = 3 DIGITS */ + + status_code = strchr(status_line, ' ') + sizeof(char); + if (strspn(status_code, "1234567890") != 3) { + die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line); + } + + http_status = atoi(status_code); + + /* check the return code */ + + if (http_status >= 600 || http_status < 100) { + die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line); + } + /* server errors result in a critical state */ + else if (http_status >= 500) { + xasprintf(&msg, _("%s - "), status_line); + result = STATE_CRITICAL; + } + /* client errors result in a warning state */ + else if (http_status >= 400) { + xasprintf(&msg, _("%s - "), status_line); + result = max_state_alt(STATE_WARNING, result); + } + /* check redirected page if specified */ + else if (http_status >= 300) { + + if (onredirect == STATE_DEPENDENT) { + redir(header, status_line); + } else { + result = max_state_alt(onredirect, result); + } + xasprintf(&msg, _("%s - "), status_line); + } /* end if (http_status >= 300) */ + else { + /* Print OK status anyway */ + xasprintf(&msg, _("%s - "), status_line); + } + + } /* end else (server_expect_yn) */ + + /* reset the alarm - must be called *after* redir or we'll never die on redirects! */ + alarm(0); + + if (maximum_age >= 0) { + result = max_state_alt(check_document_dates(header, &msg), result); + } + + /* Page and Header content checks go here */ + if (strlen(header_expect) > 0) { + if (strstr(header, header_expect) == NULL) { + // We did not find the header, the rest is for building the output and setting the state + char output_header_search[30] = ""; + + strncpy(&output_header_search[0], header_expect, sizeof(output_header_search)); + + if (output_header_search[sizeof(output_header_search) - 1] != '\0') { + bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); + } + + xasprintf(&msg, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, + output_header_search, use_ssl ? "https" : "http", + host_name ? host_name : server_address, server_port, server_url); + + result = STATE_CRITICAL; + } + } + + // At this point we should test if the content is chunked and unchunk it, so + // it can be searched (and possibly printed) + const char *chunked_header_regex_string = "Transfer-Encoding: *chunked *"; + regex_t chunked_header_regex; + + if (regcomp(&chunked_header_regex, chunked_header_regex_string, REG_ICASE)) { + die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), + "Failed to compile chunked_header_regex regex"); + } + + regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF + // it was found + + if (!no_body && regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) { + if (verbose) { + printf("Found chunked content\n"); + } + // We actually found the chunked header + char *tmp = unchunk_content(page); + if (tmp == NULL) { + die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), + "Failed to unchunk message body"); + } + page = tmp; + } + + if (strlen(string_expect) > 0) { + if (!strstr(page, string_expect)) { + // We found the string the body, the rest is for building the output + char output_string_search[30] = ""; + strncpy(&output_string_search[0], string_expect, sizeof(output_string_search)); + if (output_string_search[sizeof(output_string_search) - 1] != '\0') { + bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); + } + xasprintf(&msg, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, + output_string_search, use_ssl ? "https" : "http", + host_name ? host_name : server_address, server_port, server_url); + result = STATE_CRITICAL; + } + } + + if (strlen(regexp) > 0) { + errcode = regexec(&preg, page, REGS, pmatch, 0); + if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) { + /* OK - No-op to avoid changing the logic around it */ + result = max_state_alt(STATE_OK, result); + } else if ((errcode == REG_NOMATCH && invert_regex == 0) || + (errcode == 0 && invert_regex == 1)) { + if (invert_regex == 0) { + xasprintf(&msg, _("%spattern not found, "), msg); + } else { + xasprintf(&msg, _("%spattern found, "), msg); + } + result = state_regex; + } else { + /* FIXME: Shouldn't that be UNKNOWN? */ + regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); + xasprintf(&msg, _("%sExecute Error: %s, "), msg, errbuf); + result = STATE_CRITICAL; + } + } + + /* make sure the page is of an appropriate size */ + /* page_len = get_content_length(header); */ + /* FIXME: Will this work with -N ? IMHO we should use + * get_content_length(header) and always check if it's different than the + * returned pagesize + */ + /* FIXME: IIRC pagesize returns headers - shouldn't we make + * it == get_content_length(header) ?? + */ + page_len = pagesize; + if ((max_page_len > 0) && (page_len > max_page_len)) { + xasprintf(&msg, _("%spage size %d too large, "), msg, page_len); + result = max_state_alt(STATE_WARNING, result); + } else if ((min_page_len > 0) && (page_len < min_page_len)) { + xasprintf(&msg, _("%spage size %d too small, "), msg, page_len); + result = max_state_alt(STATE_WARNING, result); + } + + /* Cut-off trailing characters */ + if (msg[strlen(msg) - 2] == ',') { + msg[strlen(msg) - 2] = '\0'; + } else { + msg[strlen(msg) - 3] = '\0'; + } + + /* check elapsed time */ + if (show_extended_perfdata) { + xasprintf( + &msg, _("%s - %d bytes in %.3f second response time %s|%s %s %s %s %s %s %s"), msg, + page_len, elapsed_time, (display_html ? "" : ""), perfd_time(elapsed_time), + perfd_size(page_len), perfd_time_connect(elapsed_time_connect), + use_ssl ? perfd_time_ssl(elapsed_time_ssl) : "", + perfd_time_headers(elapsed_time_headers), perfd_time_firstbyte(elapsed_time_firstbyte), + perfd_time_transfer(elapsed_time_transfer)); + } else { + xasprintf(&msg, _("%s - %d bytes in %.3f second response time %s|%s %s"), msg, page_len, + elapsed_time, (display_html ? "" : ""), perfd_time(elapsed_time), + perfd_size(page_len)); + } + + if (show_body) { + xasprintf(&msg, _("%s\n%s"), msg, page); + } + + result = max_state_alt(get_status(elapsed_time, thlds), result); + + die(result, "HTTP %s: %s\n", state_text(result), msg); + /* die failed? */ + return STATE_UNKNOWN; } /* Receivces a pointer to the beginning of the body of a HTTP message @@ -1398,94 +1445,95 @@ check_http (void) * The result must be freed by the caller. */ char *unchunk_content(const char *content) { - // https://en.wikipedia.org/wiki/Chunked_transfer_encoding - // https://www.rfc-editor.org/rfc/rfc7230#section-4.1 - char *result = NULL; - char *start_of_chunk; - char* end_of_chunk; - long size_of_chunk; - const char *pointer = content; - char *endptr; - long length_of_chunk = 0; - size_t overall_size = 0; - - while (true) { - size_of_chunk = strtol(pointer, &endptr, 16); - if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) { - // Apparently underflow or overflow, should not happen - if (verbose) { - printf("Got an underflow or overflow from strtol at: %u\n", __LINE__); - } - return NULL; - } - if (endptr == pointer) { - // Apparently this was not a number - if (verbose) { - printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__); - } - return NULL; - } - - // So, we got the length of the chunk - if (*endptr == ';') { - // Chunk extension starts here - while (*endptr != '\r') { - endptr++; - } - } - - start_of_chunk = endptr + 2; - end_of_chunk = start_of_chunk + size_of_chunk; - length_of_chunk = (long)(end_of_chunk - start_of_chunk); - pointer = end_of_chunk + 2; //Next number should be here - - if (length_of_chunk == 0) { - // Chunk length is 0, so this is the last one - break; - } - - overall_size += length_of_chunk; - - if (result == NULL) { - // Size of the chunk plus the ending NULL byte - result = (char *)malloc(length_of_chunk +1); - if (result == NULL) { - if (verbose) { - printf("Failed to allocate memory for unchunked body\n"); - } - return NULL; - } - } else { - // Enlarge memory to the new size plus the ending NULL byte - void *tmp = realloc(result, overall_size +1); - if (tmp == NULL) { - if (verbose) { - printf("Failed to allocate memory for unchunked body\n"); - } - return NULL; - } else { - result = tmp; - } - } - - memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk); - } - - if (overall_size == 0 && result == NULL) { - // We might just have received the end chunk without previous content, so result is never allocated - result = calloc(1, sizeof(char)); - // No error handling here, we can only return NULL anyway - } else { - result[overall_size] = '\0'; - } - return result; + // https://en.wikipedia.org/wiki/Chunked_transfer_encoding + // https://www.rfc-editor.org/rfc/rfc7230#section-4.1 + char *result = NULL; + char *start_of_chunk; + char *end_of_chunk; + long size_of_chunk; + const char *pointer = content; + char *endptr; + long length_of_chunk = 0; + size_t overall_size = 0; + + while (true) { + size_of_chunk = strtol(pointer, &endptr, 16); + if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) { + // Apparently underflow or overflow, should not happen + if (verbose) { + printf("Got an underflow or overflow from strtol at: %u\n", __LINE__); + } + return NULL; + } + if (endptr == pointer) { + // Apparently this was not a number + if (verbose) { + printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__); + } + return NULL; + } + + // So, we got the length of the chunk + if (*endptr == ';') { + // Chunk extension starts here + while (*endptr != '\r') { + endptr++; + } + } + + start_of_chunk = endptr + 2; + end_of_chunk = start_of_chunk + size_of_chunk; + length_of_chunk = (long)(end_of_chunk - start_of_chunk); + pointer = end_of_chunk + 2; // Next number should be here + + if (length_of_chunk == 0) { + // Chunk length is 0, so this is the last one + break; + } + + overall_size += length_of_chunk; + + if (result == NULL) { + // Size of the chunk plus the ending NULL byte + result = (char *)malloc(length_of_chunk + 1); + if (result == NULL) { + if (verbose) { + printf("Failed to allocate memory for unchunked body\n"); + } + return NULL; + } + } else { + // Enlarge memory to the new size plus the ending NULL byte + void *tmp = realloc(result, overall_size + 1); + if (tmp == NULL) { + if (verbose) { + printf("Failed to allocate memory for unchunked body\n"); + } + return NULL; + } + result = tmp; + } + + memcpy(result + (overall_size - size_of_chunk), start_of_chunk, size_of_chunk); + } + + if (overall_size == 0 && result == NULL) { + // We might just have received the end chunk without previous content, so result is never + // allocated + result = calloc(1, sizeof(char)); + // No error handling here, we can only return NULL anyway + } else { + result[overall_size] = '\0'; + } + return result; } /* per RFC 2396 */ #define URI_HTTP "%5[HTPShtps]" #define URI_HOST "%255[-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" #define URI_PORT "%6d" /* MAX_PORT's width is 5 chars, 6 to detect overflow */ -#define URI_PATH "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" +#define URI_PATH \ + "%[-_.!~*'();/?:@&=+$,%#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]" #define HD1 URI_HTTP "://" URI_HOST ":" URI_PORT "/" URI_PATH #define HD2 URI_HTTP "://" URI_HOST "/" URI_PATH #define HD3 URI_HTTP "://" URI_HOST ":" URI_PORT @@ -1494,424 +1542,431 @@ char *unchunk_content(const char *content) { #define HD5 "//" URI_HOST "/" URI_PATH #define HD6 URI_PATH -void -redir (char *pos, char *status_line) -{ - int i = 0; - char *x; - char xx[2]; - char type[6]; - char *addr; - char *url; - - addr = malloc (MAX_IPV4_HOSTLENGTH + 1); - if (addr == NULL) - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n")); - - memset(addr, 0, MAX_IPV4_HOSTLENGTH); - url = malloc (strcspn (pos, "\r\n")); - if (url == NULL) - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); - - while (pos) { - sscanf (pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i); - if (i == 0) { - pos += (size_t) strcspn (pos, "\r\n"); - pos += (size_t) strspn (pos, "\r\n"); - if (strlen(pos) == 0) - die (STATE_UNKNOWN, - _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"), - status_line, (display_html ? "" : "")); - continue; - } - - pos += i; - pos += strspn (pos, " \t"); - - /* - * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by - * preceding each extra line with at least one SP or HT.'' - */ - for (; (i = strspn (pos, "\r\n")); pos += i) { - pos += i; - if (!(i = strspn (pos, " \t"))) { - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"), - display_html ? "" : ""); - } - } - - url = realloc (url, strcspn (pos, "\r\n") + 1); - if (url == NULL) - die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); - - /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ - if (sscanf (pos, HD1, type, addr, &i, url) == 4) { - url = prepend_slash (url); - use_ssl = server_type_check (type); - } - - /* URI_HTTP URI_HOST URI_PATH */ - else if (sscanf (pos, HD2, type, addr, url) == 3 ) { - url = prepend_slash (url); - use_ssl = server_type_check (type); - i = server_port_check (use_ssl); - } - - /* URI_HTTP URI_HOST URI_PORT */ - else if (sscanf (pos, HD3, type, addr, &i) == 3) { - strcpy (url, HTTP_URL); - use_ssl = server_type_check (type); - } - - /* URI_HTTP URI_HOST */ - else if (sscanf (pos, HD4, type, addr) == 2) { - strcpy (url, HTTP_URL); - use_ssl = server_type_check (type); - i = server_port_check (use_ssl); - } - /* URI_HTTP, URI_HOST, URI_PATH */ - else if (sscanf (pos, HD5, addr, url) == 2) { - if(use_ssl){ - strcpy (type,"https"); - } - else{ - strcpy (type, server_type); - } - xasprintf (&url, "/%s", url); - use_ssl = server_type_check (type); - i = server_port_check (use_ssl); - } - - /* URI_PATH */ - else if (sscanf (pos, HD6, url) == 1) { - /* relative url */ - if ((url[0] != '/')) { - if ((x = strrchr(server_url, '/'))) - *x = '\0'; - xasprintf (&url, "%s/%s", server_url, url); - } - i = server_port; - strcpy (type, server_type); - strcpy (addr, host_name ? host_name : server_address); - } - - else { - die (STATE_UNKNOWN, - _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), - pos, (display_html ? "" : "")); - } - - break; - - } /* end while (pos) */ - - if (++redir_depth > max_depth) - die (STATE_WARNING, - _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"), - max_depth, type, addr, i, url, (display_html ? "" : "")); - - if (server_port==i && - !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) && - (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && - !strcmp(server_url, url)) - die (STATE_CRITICAL, - _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), - type, addr, i, url, (display_html ? "" : "")); - - strcpy (server_type, type); - - free (host_name); - host_name = strndup (addr, MAX_IPV4_HOSTLENGTH); - - if (!(followsticky & STICKY_HOST)) { - free (server_address); - server_address = strndup (addr, MAX_IPV4_HOSTLENGTH); - } - if (!(followsticky & STICKY_PORT)) { - server_port = i; - } - - free (server_url); - server_url = url; - - if (server_port > MAX_PORT) - die (STATE_UNKNOWN, - _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"), - MAX_PORT, server_type, server_address, server_port, server_url, - display_html ? "" : ""); - - /* reset virtual port */ - virtual_port = server_port; - - if (verbose) - printf (_("Redirection to %s://%s:%d%s\n"), server_type, - host_name ? host_name : server_address, server_port, server_url); - - free(addr); - check_http (); -} +void redir(char *pos, char *status_line) { + int i = 0; + char *x; + char xx[2]; + char type[6]; + char *addr; + char *url; + + addr = malloc(MAX_IPV4_HOSTLENGTH + 1); + if (addr == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate addr\n")); + } + + memset(addr, 0, MAX_IPV4_HOSTLENGTH); + url = malloc(strcspn(pos, "\r\n")); + if (url == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); + } + + while (pos) { + sscanf(pos, "%1[Ll]%*1[Oo]%*1[Cc]%*1[Aa]%*1[Tt]%*1[Ii]%*1[Oo]%*1[Nn]:%n", xx, &i); + if (i == 0) { + pos += (size_t)strcspn(pos, "\r\n"); + pos += (size_t)strspn(pos, "\r\n"); + if (strlen(pos) == 0) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not find redirect location - %s%s\n"), + status_line, (display_html ? "" : "")); + } + continue; + } + + pos += i; + pos += strspn(pos, " \t"); + + /* + * RFC 2616 (4.2): ``Header fields can be extended over multiple lines by + * preceding each extra line with at least one SP or HT.'' + */ + for (; (i = strspn(pos, "\r\n")); pos += i) { + pos += i; + if (!(i = strspn(pos, " \t"))) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Empty redirect location%s\n"), + display_html ? "" : ""); + } + } + + url = realloc(url, strcspn(pos, "\r\n") + 1); + if (url == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); + } + + /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */ + if (sscanf(pos, HD1, type, addr, &i, url) == 4) { + url = prepend_slash(url); + use_ssl = server_type_check(type); + } + + /* URI_HTTP URI_HOST URI_PATH */ + else if (sscanf(pos, HD2, type, addr, url) == 3) { + url = prepend_slash(url); + use_ssl = server_type_check(type); + i = server_port_check(use_ssl); + } + + /* URI_HTTP URI_HOST URI_PORT */ + else if (sscanf(pos, HD3, type, addr, &i) == 3) { + strcpy(url, HTTP_URL); + use_ssl = server_type_check(type); + } + + /* URI_HTTP URI_HOST */ + else if (sscanf(pos, HD4, type, addr) == 2) { + strcpy(url, HTTP_URL); + use_ssl = server_type_check(type); + i = server_port_check(use_ssl); + } + /* URI_HTTP, URI_HOST, URI_PATH */ + else if (sscanf(pos, HD5, addr, url) == 2) { + if (use_ssl) { + strcpy(type, "https"); + } else { + strcpy(type, server_type); + } + xasprintf(&url, "/%s", url); + use_ssl = server_type_check(type); + i = server_port_check(use_ssl); + } + + /* URI_PATH */ + else if (sscanf(pos, HD6, url) == 1) { + /* relative url */ + if ((url[0] != '/')) { + if ((x = strrchr(server_url, '/'))) { + *x = '\0'; + } + xasprintf(&url, "%s/%s", server_url, url); + } + i = server_port; + strcpy(type, server_type); + strcpy(addr, host_name ? host_name : server_address); + } + + else { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location - %s%s\n"), pos, + (display_html ? "" : "")); + } + + break; + + } /* end while (pos) */ + + if (++redir_depth > max_depth) { + die(STATE_WARNING, + _("HTTP WARNING - maximum redirection depth %d exceeded - %s://%s:%d%s%s\n"), max_depth, + type, addr, i, url, (display_html ? "" : "")); + } + if (server_port == i && !strncmp(server_address, addr, MAX_IPV4_HOSTLENGTH) && + (host_name && !strncmp(host_name, addr, MAX_IPV4_HOSTLENGTH)) && !strcmp(server_url, url)) { + die(STATE_CRITICAL, + _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), type, + addr, i, url, (display_html ? "" : "")); + } -bool -server_type_check (const char *type) -{ - if (strcmp (type, "https")) - return false; - else - return true; + strcpy(server_type, type); + + free(host_name); + host_name = strndup(addr, MAX_IPV4_HOSTLENGTH); + + if (!(followsticky & STICKY_HOST)) { + free(server_address); + server_address = strndup(addr, MAX_IPV4_HOSTLENGTH); + } + if (!(followsticky & STICKY_PORT)) { + server_port = i; + } + + free(server_url); + server_url = url; + + if (server_port > MAX_PORT) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s://%s:%d%s%s\n"), + MAX_PORT, server_type, server_address, server_port, server_url, + display_html ? "" : ""); + } + + /* reset virtual port */ + virtual_port = server_port; + + if (verbose) { + printf(_("Redirection to %s://%s:%d%s\n"), server_type, + host_name ? host_name : server_address, server_port, server_url); + } + + free(addr); + check_http(); } -int -server_port_check (int ssl_flag) -{ - if (ssl_flag) - return HTTPS_PORT; - else - return HTTP_PORT; +bool server_type_check(const char *type) { return (!(bool)strcmp(type, "https")); } + +int server_port_check(int ssl_flag) { + if (ssl_flag) { + return HTTPS_PORT; + } + return HTTP_PORT; } -char *perfd_time (double elapsed_time) -{ - return fperfdata ("time", elapsed_time, "s", - thlds->warning?true:false, thlds->warning?thlds->warning->end:0, - thlds->critical?true:false, thlds->critical?thlds->critical->end:0, - true, 0, true, socket_timeout); +char *perfd_time(double elapsed_time) { + return fperfdata("time", elapsed_time, "s", thlds->warning, + thlds->warning ? thlds->warning->end : 0, thlds->critical, + thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); } -char *perfd_time_connect (double elapsed_time_connect) -{ - return fperfdata ("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, socket_timeout); +char *perfd_time_connect(double elapsed_time_connect) { + return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } -char *perfd_time_ssl (double elapsed_time_ssl) -{ - return fperfdata ("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, socket_timeout); +char *perfd_time_ssl(double elapsed_time_ssl) { + return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } -char *perfd_time_headers (double elapsed_time_headers) -{ - return fperfdata ("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, socket_timeout); +char *perfd_time_headers(double elapsed_time_headers) { + return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, + socket_timeout); } -char *perfd_time_firstbyte (double elapsed_time_firstbyte) -{ - return fperfdata ("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, true, socket_timeout); +char *perfd_time_firstbyte(double elapsed_time_firstbyte) { + return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, + true, socket_timeout); } -char *perfd_time_transfer (double elapsed_time_transfer) -{ - return fperfdata ("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, true, socket_timeout); +char *perfd_time_transfer(double elapsed_time_transfer) { + return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, + true, socket_timeout); } -char *perfd_size (int page_len) -{ - return perfdata ("size", page_len, "B", - (min_page_len>0?true:false), min_page_len, - (min_page_len>0?true:false), 0, - true, 0, false, 0); +char *perfd_size(int page_len) { + return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, + true, 0, false, 0); } -void -print_help (void) -{ - print_revision (progname, NP_VERSION); +void print_help(void) { + print_revision(progname, NP_VERSION); - printf ("Copyright (c) 1999 Ethan Galstad \n"); - printf (COPYRIGHT, copyright, email); + printf("Copyright (c) 1999 Ethan Galstad \n"); + printf(COPYRIGHT, copyright, email); - printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); - printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); - printf ("%s\n", _("strings and regular expressions, check connection times, and report on")); - printf ("%s\n", _("certificate expiration times.")); + printf("%s\n", _("This plugin tests the HTTP service on the specified host. It can test")); + printf("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for")); + printf("%s\n", _("strings and regular expressions, check connection times, and report on")); + printf("%s\n", _("certificate expiration times.")); - printf ("\n"); - printf ("%s\n", _("ATTENTION!")); - printf ("\n"); - printf ("%s\n", _("THIS PLUGIN IS DEPRECATED. The functionality was reimplemented by the")); - printf ("%s\n", _("check_curl plugin, which can be used as a drop-in replacement. You should")); - printf ("%s\n", _("migrate your checks over to check_curl, because check_http is going to be")); - printf ("%s\n", _("removed sooner than later. Just replace check_http with check_curl in your")); - printf ("%s\n", _("check command definitions.")); - printf ("%s\n", _("Report issues to: https://github.com/monitoring-plugins/monitoring-plugins/issues")); + printf("\n"); + printf("%s\n", _("ATTENTION!")); + printf("\n"); + printf("%s\n", _("THIS PLUGIN IS DEPRECATED. The functionality was reimplemented by the")); + printf("%s\n", _("check_curl plugin, which can be used as a drop-in replacement. You should")); + printf("%s\n", _("migrate your checks over to check_curl, because check_http is going to be")); + printf("%s\n", _("removed sooner than later. Just replace check_http with check_curl in your")); + printf("%s\n", _("check command definitions.")); + printf("%s\n", + _("Report issues to: https://github.com/monitoring-plugins/monitoring-plugins/issues")); - printf ("\n\n"); + printf("\n\n"); - print_usage (); + print_usage(); #ifdef HAVE_SSL - printf (_("In the first form, make an HTTP request.")); - printf (_("In the second form, connect to the server and check the TLS certificate.")); + printf(_("In the first form, make an HTTP request.")); + printf(_("In the second form, connect to the server and check the TLS certificate.")); #endif - printf (_("NOTE: One or both of -H and -I must be specified")); + printf(_("NOTE: One or both of -H and -I must be specified")); - printf ("\n"); + printf("\n"); - printf (UT_HELP_VRSN); - printf (UT_EXTRA_OPTS); + printf(UT_HELP_VRSN); + printf(UT_EXTRA_OPTS); - printf (" %s\n", "-H, --hostname=ADDRESS"); - printf (" %s\n", _("Host name argument for servers using host headers (virtual host)")); - printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); - printf (" %s\n", "-I, --IP-address=ADDRESS"); - printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup).")); - printf (" %s\n", "-p, --port=INTEGER"); - printf (" %s", _("Port number (default: ")); - printf ("%d)\n", HTTP_PORT); + printf(" %s\n", "-H, --hostname=ADDRESS"); + printf(" %s\n", _("Host name argument for servers using host headers (virtual host)")); + printf(" %s\n", _("Append a port to include it in the header (eg: example.com:5000)")); + printf(" %s\n", "-I, --IP-address=ADDRESS"); + printf(" %s\n", + _("IP address or name (use numeric address if possible to bypass DNS lookup).")); + printf(" %s\n", "-p, --port=INTEGER"); + printf(" %s", _("Port number (default: ")); + printf("%d)\n", HTTP_PORT); - printf (UT_IPv46); + printf(UT_IPv46); #ifdef HAVE_SSL - printf (" %s\n", "-S, --ssl=VERSION[+]"); - printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); - printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); - printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted.")); - printf (" %s\n", "--sni"); - printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); - printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); - printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443")); - printf (" %s\n", _("(when this option is used the URL is not checked by default. You can use")); - printf (" %s\n", _(" --continue-after-certificate to override this behavior)")); - printf (" %s\n", "--continue-after-certificate"); - printf (" %s\n", _("Allows the HTTP check to continue after performing the certificate check.")); - printf (" %s\n", _("Does nothing unless -C is used.")); - printf (" %s\n", "-J, --client-cert=FILE"); - printf (" %s\n", _("Name of file that contains the client certificate (PEM format)")); - printf (" %s\n", _("to be used in establishing the SSL session")); - printf (" %s\n", "-K, --private-key=FILE"); - printf (" %s\n", _("Name of file containing the private key (PEM format)")); - printf (" %s\n", _("matching the client certificate")); + printf(" %s\n", "-S, --ssl=VERSION[+]"); + printf(" %s\n", + _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents")); + printf(" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,")); + printf(" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted.")); + printf(" %s\n", "--sni"); + printf(" %s\n", _("Enable SSL/TLS hostname extension support (SNI)")); + printf(" %s\n", "-C, --certificate=INTEGER[,INTEGER]"); + printf(" %s\n", + _("Minimum number of days a certificate has to be valid. Port defaults to 443")); + printf(" %s\n", + _("(when this option is used the URL is not checked by default. You can use")); + printf(" %s\n", _(" --continue-after-certificate to override this behavior)")); + printf(" %s\n", "--continue-after-certificate"); + printf(" %s\n", + _("Allows the HTTP check to continue after performing the certificate check.")); + printf(" %s\n", _("Does nothing unless -C is used.")); + printf(" %s\n", "-J, --client-cert=FILE"); + printf(" %s\n", _("Name of file that contains the client certificate (PEM format)")); + printf(" %s\n", _("to be used in establishing the SSL session")); + printf(" %s\n", "-K, --private-key=FILE"); + printf(" %s\n", _("Name of file containing the private key (PEM format)")); + printf(" %s\n", _("matching the client certificate")); #endif - printf (" %s\n", "-e, --expect=STRING"); - printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); - printf (" %s", _("the first (status) line of the server response (default: ")); - printf ("%s)\n", HTTP_EXPECT); - printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); - printf (" %s\n", "-d, --header-string=STRING"); - printf (" %s\n", _("String to expect in the response headers")); - printf (" %s\n", "-s, --string=STRING"); - printf (" %s\n", _("String to expect in the content")); - printf (" %s\n", "-u, --url=PATH"); - printf (" %s\n", _("URL to GET or POST (default: /)")); - printf (" %s\n", "-P, --post=STRING"); - printf (" %s\n", _("URL decoded http POST data")); - printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT, CONNECT:POST)"); - printf (" %s\n", _("Set HTTP method.")); - printf (" %s\n", "-N, --no-body"); - printf (" %s\n", _("Don't wait for document body: stop reading after headers.")); - printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)")); - printf (" %s\n", "-M, --max-age=SECONDS"); - printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); - printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); - printf (" %s\n", "-T, --content-type=STRING"); - printf (" %s\n", _("specify Content-Type header media type when POSTing\n")); - - printf (" %s\n", "-l, --linespan"); - printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)")); - printf (" %s\n", "-r, --regex, --ereg=STRING"); - printf (" %s\n", _("Search page for regex STRING")); - printf (" %s\n", "-R, --eregi=STRING"); - printf (" %s\n", _("Search page for case-insensitive regex STRING")); - printf (" %s\n", "--invert-regex"); - printf (" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); - printf (" %s\n", _("can be changed with --state--regex)")); - printf (" %s\n", "--state-regex=STATE"); - printf (" %s\n", _("Return STATE if regex is found, OK if not\n")); - - printf (" %s\n", "-a, --authorization=AUTH_PAIR"); - printf (" %s\n", _("Username:password on sites with basic authentication")); - printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); - printf (" %s\n", _("Username:password on proxy-servers with basic authentication")); - printf (" %s\n", "-A, --useragent=STRING"); - printf (" %s\n", _("String to be sent in http header as \"User Agent\"")); - printf (" %s\n", "-k, --header=STRING"); - printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers")); - printf (" %s\n", "-E, --extended-perfdata"); - printf (" %s\n", _("Print additional performance data")); - printf (" %s\n", "-B, --show-body"); - printf (" %s\n", _("Print body content below status line")); - printf (" %s\n", "-L, --link"); - printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); - printf (" %s\n", "-f, --onredirect="); - printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); - printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); - printf (" %s\n", "--max-redirs=INTEGER"); - printf (" %s", _("Maximal number of redirects (default: ")); - printf ("%d)\n", DEFAULT_MAX_REDIRS); - printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); - printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)")); - printf (UT_WARN_CRIT); - - printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); - - printf (UT_VERBOSE); - - printf ("\n"); - printf ("%s\n", _("Notes:")); - printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); - printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); - printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); - printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); - printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN")); - printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); + printf(" %s\n", "-e, --expect=STRING"); + printf(" %s\n", _("Comma-delimited list of strings, at least one of them is expected in")); + printf(" %s", _("the first (status) line of the server response (default: ")); + printf("%s)\n", HTTP_EXPECT); + printf(" %s\n", + _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)")); + printf(" %s\n", "-d, --header-string=STRING"); + printf(" %s\n", _("String to expect in the response headers")); + printf(" %s\n", "-s, --string=STRING"); + printf(" %s\n", _("String to expect in the content")); + printf(" %s\n", "-u, --url=PATH"); + printf(" %s\n", _("URL to GET or POST (default: /)")); + printf(" %s\n", "-P, --post=STRING"); + printf(" %s\n", _("URL decoded http POST data")); + printf(" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, " + "CONNECT, CONNECT:POST)"); + printf(" %s\n", _("Set HTTP method.")); + printf(" %s\n", "-N, --no-body"); + printf(" %s\n", _("Don't wait for document body: stop reading after headers.")); + printf(" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)")); + printf(" %s\n", "-M, --max-age=SECONDS"); + printf(" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); + printf(" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); + printf(" %s\n", "-T, --content-type=STRING"); + printf(" %s\n", _("specify Content-Type header media type when POSTing\n")); + + printf(" %s\n", "-l, --linespan"); + printf(" %s\n", _("Allow regex to span newlines (must precede -r or -R)")); + printf(" %s\n", "-r, --regex, --ereg=STRING"); + printf(" %s\n", _("Search page for regex STRING")); + printf(" %s\n", "-R, --eregi=STRING"); + printf(" %s\n", _("Search page for case-insensitive regex STRING")); + printf(" %s\n", "--invert-regex"); + printf(" %s\n", _("Return STATE if found, OK if not (STATE is CRITICAL, per default)")); + printf(" %s\n", _("can be changed with --state--regex)")); + printf(" %s\n", "--state-regex=STATE"); + printf(" %s\n", _("Return STATE if regex is found, OK if not\n")); + + printf(" %s\n", "-a, --authorization=AUTH_PAIR"); + printf(" %s\n", _("Username:password on sites with basic authentication")); + printf(" %s\n", "-b, --proxy-authorization=AUTH_PAIR"); + printf(" %s\n", _("Username:password on proxy-servers with basic authentication")); + printf(" %s\n", "-A, --useragent=STRING"); + printf(" %s\n", _("String to be sent in http header as \"User Agent\"")); + printf(" %s\n", "-k, --header=STRING"); + printf( + " %s\n", + _("Any other tags to be sent in http header. Use multiple times for additional headers")); + printf(" %s\n", "-E, --extended-perfdata"); + printf(" %s\n", _("Print additional performance data")); + printf(" %s\n", "-B, --show-body"); + printf(" %s\n", _("Print body content below status line")); + printf(" %s\n", "-L, --link"); + printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); + printf(" %s\n", "-f, --onredirect="); + printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); + printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); + printf(" %s\n", "--max-redirs=INTEGER"); + printf(" %s", _("Maximal number of redirects (default: ")); + printf("%d)\n", DEFAULT_MAX_REDIRS); + printf(" %s\n", "-m, --pagesize=INTEGER<:INTEGER>"); + printf(" %s\n", + _("Minimum page size required (bytes) : Maximum page size required (bytes)")); + printf(UT_WARN_CRIT); + + printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + + printf(UT_VERBOSE); + + printf("\n"); + printf("%s\n", _("Notes:")); + printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); + printf(" %s\n", + _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL")); + printf(" %s\n", + _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response")); + printf(" %s\n", _("messages from the host result in STATE_WARNING return values. If you are")); + printf(" %s\n", + _("checking a virtual server that uses 'host headers' you must supply the FQDN")); + printf(" %s\n", _("(fully qualified domain name) as the [host_name] argument.")); #ifdef HAVE_SSL - printf ("\n"); - printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to")); - printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 ")); - printf (" %s\n", _("certificate is still valid for the specified number of days.")); - printf ("\n"); - printf (" %s\n", _("Please note that this plugin does not check if the presented server")); - printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate")); - printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs.")); - printf ("\n"); - printf ("%s\n", _("Examples:")); - printf (" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com"); - printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); - printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); - printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); - printf (" %s\n", _("a STATE_CRITICAL will be returned.")); - printf ("\n"); - printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14"); - printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); - printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); - printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); - printf (" %s\n\n", _("the certificate is expired.")); - printf ("\n"); - printf (" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14"); - printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); - printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than")); - printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); - printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); - - printf (" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); - printf (" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com ")); - printf (" %s\n", _("all these options are needed: -I -p -u -S(sl) -j CONNECT -H ")); - printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds")); - printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); - printf (" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can set the method used")); - printf (" %s\n", _("inside the proxied connection: -j CONNECT:POST")); + printf("\n"); + printf(" %s\n", _("This plugin can also check whether an SSL enabled web server is able to")); + printf(" %s\n", _("serve content (optionally within a specified time) or whether the X509 ")); + printf(" %s\n", _("certificate is still valid for the specified number of days.")); + printf("\n"); + printf(" %s\n", _("Please note that this plugin does not check if the presented server")); + printf(" %s\n", _("certificate matches the hostname of the server, or if the certificate")); + printf(" %s\n", _("has a valid chain of trust to one of the locally installed CAs.")); + printf("\n"); + printf("%s\n", _("Examples:")); + printf(" %s\n\n", "CHECK CONTENT: check_http -w 5 -c 10 --ssl -H www.verisign.com"); + printf(" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,")); + printf(" %s\n", + _("a STATE_OK will be returned. When the server returns its content but exceeds")); + printf(" %s\n", + _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); + printf(" %s\n", _("a STATE_CRITICAL will be returned.")); + printf("\n"); + printf(" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 14"); + printf(" %s\n", + _("When the certificate of 'www.verisign.com' is valid for more than 14 days,")); + printf(" %s\n", + _("a STATE_OK is returned. When the certificate is still valid, but for less than")); + printf(" %s\n", + _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when")); + printf(" %s\n\n", _("the certificate is expired.")); + printf("\n"); + printf(" %s\n\n", "CHECK CERTIFICATE: check_http -H www.verisign.com -C 30,14"); + printf(" %s\n", + _("When the certificate of 'www.verisign.com' is valid for more than 30 days,")); + printf(" %s\n", + _("a STATE_OK is returned. When the certificate is still valid, but for less than")); + printf(" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned.")); + printf(" %s\n", + _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days")); + + printf(" %s\n\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: "); + printf(" %s\n", _("check_http -I 192.168.100.35 -p 80 -u https://www.verisign.com/ -S -j " + "CONNECT -H www.verisign.com ")); + printf(" %s\n", _("all these options are needed: -I -p -u " + "-S(sl) -j CONNECT -H ")); + printf(" %s\n", + _("a STATE_OK will be returned. When the server returns its content but exceeds")); + printf(" %s\n", + _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,")); + printf(" %s\n", _("a STATE_CRITICAL will be returned. By adding a colon to the method you can " + "set the method used")); + printf(" %s\n", _("inside the proxied connection: -j CONNECT:POST")); #endif - printf (UT_SUPPORT); - + printf(UT_SUPPORT); } - - -void -print_usage (void) -{ - printf ("%s\n", _("Usage:")); - printf (" %s -H | -I [-u ] [-p ]\n",progname); - printf (" [-J ] [-K ]\n"); - printf (" [-w ] [-c ] [-t ] [-L] [-E] [-a auth]\n"); - printf (" [-b proxy_auth] [-f ]\n"); - printf (" [-e ] [-d string] [-s string] [-l] [-r | -R ]\n"); - printf (" [-P string] [-m :] [-4|-6] [-N] [-M ]\n"); - printf (" [-A string] [-k string] [-S ] [--sni]\n"); - printf (" [-T ] [-j method]\n"); - printf (" %s -H | -I -C [,]\n",progname); - printf (" [-p ] [-t ] [-4|-6] [--sni]\n"); +void print_usage(void) { + printf("%s\n", _("Usage:")); + printf(" %s -H | -I [-u ] [-p ]\n", progname); + printf(" [-J ] [-K ]\n"); + printf(" [-w ] [-c ] [-t ] [-L] [-E] [-a auth]\n"); + printf(" [-b proxy_auth] [-f ]\n"); + printf(" [-e ] [-d string] [-s string] [-l] [-r | -R ]\n"); + printf(" [-P string] [-m :] [-4|-6] [-N] [-M ]\n"); + printf(" [-A string] [-k string] [-S ] [--sni]\n"); + printf(" [-T ] [-j method]\n"); + printf(" %s -H | -I -C [,]\n", progname); + printf(" [-p ] [-t ] [-4|-6] [--sni]\n"); } -- cgit v1.2.3-74-g34f1 From 01f353228424376b25f20d9b105aaf5b3c7a6f65 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:07:55 +0200 Subject: check_curl: remove another global variable --- plugins/check_curl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 76dcbfd8..f28bcdfc 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -145,7 +145,6 @@ check_curl_global_state global_state = { .curl = NULL, }; -static long httpReturnCode; static char errbuf[MAX_INPUT_BUFFER]; static char msg[DEFAULT_BUFFER_SIZE]; typedef union { @@ -257,7 +256,6 @@ int main(int argc, char **argv) { #ifdef HAVE_SSL # ifdef USE_OPENSSL - int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { (void)preverify_ok; /* TODO: we get all certificates of the chain, so which ones @@ -297,7 +295,6 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { return CURLE_OK; } - # endif /* USE_OPENSSL */ #endif /* HAVE_SSL */ @@ -1108,11 +1105,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat perfstring); /* we cannot know the major/minor version here for sure as we cannot parse the first * line */ - die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x %ld unknown - %s", httpReturnCode, msg); + die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x unknown - %s", msg); } global_state.status_line_initialized = true; /* get result code from cURL */ + long httpReturnCode; handle_curl_option_return_code( curl_easy_getinfo(global_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), "CURLINFO_RESPONSE_CODE"); -- cgit v1.2.3-74-g34f1 From c06ea4e44caee74486e2931154021a0d0e6c8066 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:08:52 +0200 Subject: check_curl: pre compile regex for string matching --- plugins/check_curl.c | 7 ++++--- plugins/check_curl.d/config.h | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index f28bcdfc..ba38854a 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1261,9 +1261,8 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strlen(config.regexp)) { - regex_t preg; regmatch_t pmatch[REGS]; - int errcode = regexec(&preg, global_state.body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&config.compiled_regex, global_state.body_buf.buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1284,7 +1283,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } result = config.state_regex; } else { - regerror(errcode, &preg, errbuf, MAX_INPUT_BUFFER); + regerror(errcode, &config.compiled_regex, errbuf, MAX_INPUT_BUFFER); char tmp[DEFAULT_BUFFER_SIZE]; @@ -1969,6 +1968,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.errorcode = ERROR; return result; } + + result.config.compiled_regex = preg; break; case INVERT_REGEX: result.config.invert_regex = true; diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 9de68713..9acd4048 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -8,6 +8,7 @@ #include #include #include "curl/curl.h" +#include "regex.h" enum { MAX_RE_SIZE = 1024, @@ -87,7 +88,13 @@ typedef struct { char *cookie_jar_file; int maximum_age; + + // the original regex string from the command line char regexp[MAX_RE_SIZE]; + + // the compiled regex for usage later + regex_t compiled_regex; + mp_state_enum state_regex; bool invert_regex; bool verify_peer_and_host; @@ -136,6 +143,7 @@ check_curl_config check_curl_config_init() { .maximum_age = -1, .regexp = {}, + .compiled_regex = {}, .state_regex = STATE_CRITICAL, .invert_regex = false, .verify_peer_and_host = false, -- cgit v1.2.3-74-g34f1 From 669441d16c454974622d5d4d1e3aefac5428a26a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:39:42 +0200 Subject: check_curl: set http port to 80 by default --- plugins/check_curl.d/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 9acd4048..25cfeda3 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -57,7 +57,7 @@ check_curl_working_state check_curl_working_state_init() { .http_method = NULL, .http_post_data = NULL, .virtualPort = 0, - .serverPort = 0, + .serverPort = HTTP_PORT, .use_ssl = false, .no_body = false, }; -- cgit v1.2.3-74-g34f1 From 2c81d1257bd2cccb57c2e9f02fbfc42c70323217 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:06:48 +0200 Subject: check_curl: remove goto logic --- plugins/check_curl.c | 260 +++++++++++++++++++++++++++------------------------ 1 file changed, 138 insertions(+), 122 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index ba38854a..897ca9e1 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1628,6 +1628,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { char *critical_thresholds = NULL; int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; bool specify_port = false; + bool enable_tls = false; + char *tls_option_optarg = NULL; while (true) { int option_index = getopt_long( @@ -1748,152 +1750,82 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.display_html = false; break; case 'C': /* Check SSL cert validity */ -#ifdef LIBCURL_FEATURE_SSL - char *temp; - if ((temp = strchr(optarg, ',')) != NULL) { - *temp = '\0'; - if (!is_intnonneg(optarg)) { - usage2(_("Invalid certificate expiration period"), optarg); - } - result.config.days_till_exp_warn = atoi(optarg); - *temp = ','; - temp++; - if (!is_intnonneg(temp)) { - usage2(_("Invalid certificate expiration period"), temp); - } - result.config.days_till_exp_crit = atoi(temp); - } else { - result.config.days_till_exp_crit = 0; - if (!is_intnonneg(optarg)) { - usage2(_("Invalid certificate expiration period"), optarg); +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif + { + char *temp; + if ((temp = strchr(optarg, ',')) != NULL) { + *temp = '\0'; + if (!is_intnonneg(optarg)) { + usage2(_("Invalid certificate expiration period"), optarg); + } + result.config.days_till_exp_warn = atoi(optarg); + *temp = ','; + temp++; + if (!is_intnonneg(temp)) { + usage2(_("Invalid certificate expiration period"), temp); + } + result.config.days_till_exp_crit = atoi(temp); + } else { + result.config.days_till_exp_crit = 0; + if (!is_intnonneg(optarg)) { + usage2(_("Invalid certificate expiration period"), optarg); + } + result.config.days_till_exp_warn = atoi(optarg); } - result.config.days_till_exp_warn = atoi(optarg); + result.config.check_cert = true; + enable_tls = true; } - result.config.check_cert = true; - goto enable_ssl; -#endif + break; case CONTINUE_AFTER_CHECK_CERT: /* don't stop after the certificate is checked */ #ifdef HAVE_SSL result.config.continue_after_check_cert = true; break; #endif case 'J': /* use client certificate */ -#ifdef LIBCURL_FEATURE_SSL +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif test_file(optarg); result.config.client_cert = optarg; - goto enable_ssl; -#endif + enable_tls = true; + break; case 'K': /* use client private key */ -#ifdef LIBCURL_FEATURE_SSL +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif test_file(optarg); result.config.client_privkey = optarg; - goto enable_ssl; -#endif -#ifdef LIBCURL_FEATURE_SSL + enable_tls = true; + break; case CA_CERT_OPTION: /* use CA chain file */ +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif test_file(optarg); result.config.ca_cert = optarg; - goto enable_ssl; -#endif -#ifdef LIBCURL_FEATURE_SSL + enable_tls = true; + break; case 'D': /* verify peer certificate & host */ +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); +#endif result.config.verify_peer_and_host = true; + enable_tls = true; break; -#endif case 'S': /* use SSL */ -#ifdef LIBCURL_FEATURE_SSL - { - enable_ssl: - bool got_plus = false; - result.config.initial_config.use_ssl = true; - /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. - * Only set if it's non-zero. This helps when we include multiple - * parameters, like -S and -C combinations */ - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; - if (option_index == 'S' && optarg != NULL) { - char *plus_ptr = strchr(optarg, '+'); - if (plus_ptr) { - got_plus = true; - *plus_ptr = '\0'; - } - - if (optarg[0] == '2') { - result.config.ssl_version = CURL_SSLVERSION_SSLv2; - } else if (optarg[0] == '3') { - result.config.ssl_version = CURL_SSLVERSION_SSLv3; - } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - } else if (!strcmp(optarg, "1.1")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - } else if (!strcmp(optarg, "1.2")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - } else if (!strcmp(optarg, "1.3")) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; -# else - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ - } else { - usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " - "(with optional '+' suffix)")); - } - } -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) - if (got_plus) { - switch (result.config.ssl_version) { - case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; - break; - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; - break; - } - } else { - switch (result.config.ssl_version) { - case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; - break; - case CURL_SSLVERSION_TLSv1_2: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; - break; - case CURL_SSLVERSION_TLSv1_1: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; - break; - case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; - break; - } - } -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ - if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); - } - if (!specify_port) { - result.config.initial_config.serverPort = HTTPS_PORT; - } - } break; -#else /* LIBCURL_FEATURE_SSL */ - /* -C -J and -K fall through to here without SSL */ + tls_option_optarg = optarg; + enable_tls = true; +#ifndef LIBCURL_FEATURE_SSL usage4(_("Invalid option - SSL is not available")); +#endif break; case SNI_OPTION: /* --sni is parsed, but ignored, the default is true with libcurl */ - use_sni = true; - break; +#ifndef LIBCURL_FEATURE_SSL + usage4(_("Invalid option - SSL is not available")); #endif /* LIBCURL_FEATURE_SSL */ + break; case MAX_REDIRS_OPTION: if (!is_intnonneg(optarg)) { usage2(_("Invalid max_redirs count"), optarg); @@ -2080,6 +2012,90 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } } + if (enable_tls) { + bool got_plus = false; + result.config.initial_config.use_ssl = true; + /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. + * Only set if it's non-zero. This helps when we include multiple + * parameters, like -S and -C combinations */ + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; + if (tls_option_optarg != NULL) { + char *plus_ptr = strchr(optarg, '+'); + if (plus_ptr) { + got_plus = true; + *plus_ptr = '\0'; + } + + if (optarg[0] == '2') { + result.config.ssl_version = CURL_SSLVERSION_SSLv2; + } else if (optarg[0] == '3') { + result.config.ssl_version = CURL_SSLVERSION_SSLv3; + } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + } else if (!strcmp(optarg, "1.1")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + } else if (!strcmp(optarg, "1.2")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + } else if (!strcmp(optarg, "1.3")) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) + result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; +#else + result.config.ssl_version = CURL_SSLVERSION_DEFAULT; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ + } else { + usage4(_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2, 1.3 " + "(with optional '+' suffix)")); + } + } +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) + if (got_plus) { + switch (result.config.ssl_version) { + case CURL_SSLVERSION_TLSv1_3: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + break; + case CURL_SSLVERSION_TLSv1_2: + case CURL_SSLVERSION_TLSv1_1: + case CURL_SSLVERSION_TLSv1_0: + result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; + break; + } + } else { + switch (result.config.ssl_version) { + case CURL_SSLVERSION_TLSv1_3: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + break; + case CURL_SSLVERSION_TLSv1_2: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; + break; + case CURL_SSLVERSION_TLSv1_1: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; + break; + case CURL_SSLVERSION_TLSv1_0: + result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; + break; + } + } +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ + if (verbose >= 2) { + printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); + } + if (!specify_port) { + result.config.initial_config.serverPort = HTTPS_PORT; + } + } + int option_counter = optind; if (result.config.initial_config.server_address == NULL && option_counter < argc) { -- cgit v1.2.3-74-g34f1 From 4b19852dcc62a69a5a77a45bc9deb0045812104a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:22:46 +0200 Subject: check_curl: remove the other gotos --- plugins/check_curl.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 897ca9e1..7bb67645 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -991,8 +991,13 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * parsing We only check the first certificate and assume it's the one of * the server */ - const char *raw_cert = NULL; + char *raw_cert = NULL; + bool got_first_cert = false; for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { + if (got_first_cert) { + break; + } + for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { if (verbose >= 2) { @@ -1000,11 +1005,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strncmp(slist->data, "Cert:", 5) == 0) { raw_cert = &slist->data[5]; - goto GOT_FIRST_CERT; + got_first_cert = true; + break; } } } - GOT_FIRST_CERT: + if (!raw_cert) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates from CERTINFO information - " @@ -2898,7 +2904,12 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ printf("**** REQUEST CERTIFICATES ****\n"); } + bool have_first_cert = false; for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { + if (have_first_cert) { + break; + } + for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { /* find first common name in subject, * TODO: check alternative subjects for @@ -2922,14 +2933,14 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } else if (strncasecmp(slist->data, "Expire Date:", 12) == 0) { end_date_str = &slist->data[12]; } else if (strncasecmp(slist->data, "Cert:", 5) == 0) { - goto HAVE_FIRST_CERT; + have_first_cert = true; + break; } if (verbose >= 2) { printf("%d ** %s\n", i, slist->data); } } } -HAVE_FIRST_CERT: if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); -- cgit v1.2.3-74-g34f1 From 0926e73e0e3e125555ca4b06510334f98e57646a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:32:50 +0200 Subject: sslutils.c: clang-format --- plugins/sslutils.c | 65 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/plugins/sslutils.c b/plugins/sslutils.c index 96740b3a..92e0cc84 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -37,13 +37,16 @@ static SSL *s = NULL; int np_net_ssl_init(int sd) { return np_net_ssl_init_with_hostname(sd, NULL); } -int np_net_ssl_init_with_hostname(int sd, char *host_name) { return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); } +int np_net_ssl_init_with_hostname(int sd, char *host_name) { + return np_net_ssl_init_with_hostname_and_version(sd, host_name, 0); +} int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version) { return np_net_ssl_init_with_hostname_version_and_cert(sd, host_name, version, NULL, NULL); } -int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey) { +int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, + char *privkey) { long options = 0; if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) { @@ -75,7 +78,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int # endif case MP_TLSv1_1: /* TLSv1.1 protocol */ # if !defined(SSL_OP_NO_TLSv1_1) - printf("%s\n", _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library.")); + printf("%s\n", + _("UNKNOWN - TLS protocol version 1.1 is not supported by your SSL library.")); return STATE_UNKNOWN; # else SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION); @@ -84,7 +88,8 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int # endif case MP_TLSv1_2: /* TLSv1.2 protocol */ # if !defined(SSL_OP_NO_TLSv1_2) - printf("%s\n", _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library.")); + printf("%s\n", + _("UNKNOWN - TLS protocol version 1.2 is not supported by your SSL library.")); return STATE_UNKNOWN; # else SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); @@ -145,8 +150,9 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); if ((s = SSL_new(ctx)) != NULL) { # ifdef SSL_set_tlsext_host_name - if (host_name != NULL) + if (host_name != NULL) { SSL_set_tlsext_host_name(s, host_name); + } # endif SSL_set_fd(s, sd); if (SSL_connect(s) == 1) { @@ -182,7 +188,8 @@ int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num); int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); } -int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit) { +int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit) { # ifdef USE_OPENSSL X509_NAME *subj = NULL; char timestamp[50] = ""; @@ -213,8 +220,9 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int return STATE_CRITICAL; } cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); - if (cnlen == -1) + if (cnlen == -1) { strcpy(cn, _("Unknown CN")); + } /* Retrieve timestamp of certificate */ tm = X509_get_notAfter(certificate); @@ -226,8 +234,9 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int return STATE_CRITICAL; } else { stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0'); - if (stamp.tm_year < 50) + if (stamp.tm_year < 50) { stamp.tm_year += 100; + } offset = 0; } } else { @@ -235,7 +244,8 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); return STATE_CRITICAL; } else { - stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + (tm->data[2] - '0') * 10 + (tm->data[3] - '0'); + stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + + (tm->data[2] - '0') * 10 + (tm->data[3] - '0'); stamp.tm_year -= 1900; offset = 2; } @@ -254,41 +264,48 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int setenv("TZ", "GMT", 1); tzset(); strftime(timestamp, 50, "%c %z", localtime(&tm_t)); - if (tz) + if (tz) { setenv("TZ", tz, 1); - else + } else { unsetenv("TZ"); + } tzset(); if (days_left > 0 && days_left <= days_till_exp_warn) { - printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, - days_left, timestamp); - if (days_left > days_till_exp_crit) + printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp); + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else if (days_left == 0 && time_left > 0) { - if (time_left >= 3600) + if (time_left >= 3600) { time_remaining = (int)time_left / 3600; - else + } else { time_remaining = (int)time_left / 60; + } - printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, - time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); + printf(_("%s - Certificate '%s' expires in %u %s (%s)\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, time_remaining, + time_left >= 3600 ? "hours" : "minutes", timestamp); - if (days_left > days_till_exp_crit) + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else if (time_left < 0) { printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), cn, timestamp); status = STATE_CRITICAL; } else if (days_left == 0) { - printf(_("%s - Certificate '%s' just expired (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp); - if (days_left > days_till_exp_crit) + printf(_("%s - Certificate '%s' just expired (%s).\n"), + (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, timestamp); + if (days_left > days_till_exp_crit) { status = STATE_WARNING; - else + } else { status = STATE_CRITICAL; + } } else { printf(_("OK - Certificate '%s' will expire on %s.\n"), cn, timestamp); status = STATE_OK; -- cgit v1.2.3-74-g34f1 From c7274d56274fa1a38837510b1cbde447e73e9b63 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:40:21 +0200 Subject: netutils.h: clang-format --- plugins/netutils.h | 131 ++++++++++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 66 deletions(-) diff --git a/plugins/netutils.h b/plugins/netutils.h index a95057e0..0ca524f2 100644 --- a/plugins/netutils.h +++ b/plugins/netutils.h @@ -1,63 +1,64 @@ /***************************************************************************** -* -* Monitoring Plugins net utilities include file -* -* License: GPL -* Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) -* Copyright (c) 2003-2007 Monitoring Plugins Development Team -* -* Description: -* -* This file contains common include files and function definitions -* used in many of the plugins. -* -* -* 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 Plugins net utilities include file + * + * License: GPL + * Copyright (c) 1999 Ethan Galstad (nagios@nagios.org) + * Copyright (c) 2003-2007 Monitoring Plugins Development Team + * + * Description: + * + * This file contains common include files and function definitions + * used in many of the plugins. + * + * + * 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 . + * + * + *****************************************************************************/ #ifndef _NETUTILS_H_ #define _NETUTILS_H_ #include "common.h" +#include "states.h" #include "utils.h" #include #include #include #ifdef HAVE_SYS_UN_H -# include -# ifndef UNIX_PATH_MAX - /* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */ -# define UNIX_PATH_MAX 104 -# endif /* UNIX_PATH_MAX */ -#endif /* HAVE_SYS_UN_H */ +# include +# ifndef UNIX_PATH_MAX +/* linux uses this, on sun it's hard-coded at 108 without a define, on BSD at 104 */ +# define UNIX_PATH_MAX 104 +# endif /* UNIX_PATH_MAX */ +#endif /* HAVE_SYS_UN_H */ #ifndef HOST_MAX_BYTES -# define HOST_MAX_BYTES 255 +# define HOST_MAX_BYTES 255 #endif /* process_request and wrapper macros */ -#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \ +#define process_tcp_request(addr, port, sbuf, rbuf, rsize) \ process_request(addr, port, IPPROTO_TCP, sbuf, rbuf, rsize) -#define process_udp_request(addr, port, sbuf, rbuf, rsize) \ +#define process_udp_request(addr, port, sbuf, rbuf, rsize) \ process_request(addr, port, IPPROTO_UDP, sbuf, rbuf, rsize) -int process_tcp_request2 (const char *address, int port, - const char *sbuffer, char *rbuffer, int rsize); -int process_request (const char *address, int port, int proto, - const char *sbuffer, char *rbuffer, int rsize); +int process_tcp_request2(const char *address, int port, const char *sbuffer, char *rbuffer, + int rsize); +int process_request(const char *address, int port, int proto, const char *sbuffer, char *rbuffer, + int rsize); /* my_connect and wrapper macros */ #define my_tcp_connect(addr, port, s) np_net_connect(addr, port, s, IPPROTO_TCP) @@ -65,25 +66,22 @@ int process_request (const char *address, int port, int proto, int np_net_connect(const char *address, int port, int *sd, int proto); /* send_request and wrapper macros */ -#define send_tcp_request(s, sbuf, rbuf, rsize) \ - send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize) -#define send_udp_request(s, sbuf, rbuf, rsize) \ - send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize) -int send_request (int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size); - +#define send_tcp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_TCP, sbuf, rbuf, rsize) +#define send_udp_request(s, sbuf, rbuf, rsize) send_request(s, IPPROTO_UDP, sbuf, rbuf, rsize) +int send_request(int sd, int proto, const char *send_buffer, char *recv_buffer, int recv_size); /* "is_*" wrapper macros and functions */ -bool is_host (const char *); -bool is_addr (const char *); -int dns_lookup (const char *, struct sockaddr_storage *, int); +bool is_host(const char *); +bool is_addr(const char *); +int dns_lookup(const char *, struct sockaddr_storage *, int); void host_or_die(const char *str); #define resolve_host_or_addr(addr, family) dns_lookup(addr, NULL, family) -#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) +#define is_inet_addr(addr) resolve_host_or_addr(addr, AF_INET) #ifdef USE_IPV6 -# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) -# define is_hostname(addr) resolve_host_or_addr(addr, address_family) +# define is_inet6_addr(addr) resolve_host_or_addr(addr, AF_INET6) +# define is_hostname(addr) resolve_host_or_addr(addr, address_family) #else -# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET) +# define is_hostname(addr) resolve_host_or_addr(addr, AF_INET) #endif extern unsigned int socket_timeout; @@ -92,25 +90,26 @@ extern int econn_refuse_state; extern bool was_refused; extern int address_family; -void socket_timeout_alarm_handler (int) __attribute__((noreturn)); +void socket_timeout_alarm_handler(int) __attribute__((noreturn)); /* SSL-Related functionality */ #ifdef HAVE_SSL -# define MP_SSLv2 1 -# define MP_SSLv3 2 -# define MP_TLSv1 3 -# define MP_TLSv1_1 4 -# define MP_TLSv1_2 5 -# define MP_SSLv2_OR_NEWER 6 -# define MP_SSLv3_OR_NEWER 7 -# define MP_TLSv1_OR_NEWER 8 -# define MP_TLSv1_1_OR_NEWER 9 -# define MP_TLSv1_2_OR_NEWER 10 +# define MP_SSLv2 1 +# define MP_SSLv3 2 +# define MP_TLSv1 3 +# define MP_TLSv1_1 4 +# define MP_TLSv1_2 5 +# define MP_SSLv2_OR_NEWER 6 +# define MP_SSLv3_OR_NEWER 7 +# define MP_TLSv1_OR_NEWER 8 +# define MP_TLSv1_1_OR_NEWER 9 +# define MP_TLSv1_2_OR_NEWER 10 /* maybe this could be merged with the above np_net_connect, via some flags */ int np_net_ssl_init(int sd); int np_net_ssl_init_with_hostname(int sd, char *host_name); int np_net_ssl_init_with_hostname_and_version(int sd, char *host_name, int version); -int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, char *privkey); +int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int version, char *cert, + char *privkey); void np_net_ssl_cleanup(); int np_net_ssl_write(const void *buf, int num); int np_net_ssl_read(void *buf, int num); -- cgit v1.2.3-74-g34f1 From 572ad994b136c443c5d59509a28b8343c3e40ab3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:41:22 +0200 Subject: plugins-netutils: return proper state from test functions --- plugins/netutils.h | 2 +- plugins/sslutils.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/netutils.h b/plugins/netutils.h index 0ca524f2..c53b3cef 100644 --- a/plugins/netutils.h +++ b/plugins/netutils.h @@ -113,7 +113,7 @@ int np_net_ssl_init_with_hostname_version_and_cert(int sd, char *host_name, int void np_net_ssl_cleanup(); int np_net_ssl_write(const void *buf, int num); int np_net_ssl_read(void *buf, int num); -int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); +mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); #endif /* HAVE_SSL */ #endif /* _NETUTILS_H_ */ diff --git a/plugins/sslutils.c b/plugins/sslutils.c index 92e0cc84..b20a2b2c 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -30,6 +30,7 @@ #include "common.h" #include "netutils.h" #include "../lib/monitoringplug.h" +#include "states.h" #ifdef HAVE_SSL static SSL_CTX *ctx = NULL; @@ -318,7 +319,7 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, # endif /* USE_OPENSSL */ } -int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { +mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit) { # ifdef USE_OPENSSL X509 *certificate = NULL; certificate = SSL_get_peer_certificate(s); -- cgit v1.2.3-74-g34f1 From a2ca373e2d6a9903126e152254c83245ad202ff8 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Wed, 10 Sep 2025 13:41:46 +0200 Subject: sslutils: some refactoring to improve readability --- plugins/sslutils.c | 64 ++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/plugins/sslutils.c b/plugins/sslutils.c index b20a2b2c..bea1307f 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -189,67 +189,54 @@ int np_net_ssl_write(const void *buf, int num) { return SSL_write(s, buf, num); int np_net_ssl_read(void *buf, int num) { return SSL_read(s, buf, num); } -int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, - int days_till_exp_crit) { +mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit) { # ifdef USE_OPENSSL - X509_NAME *subj = NULL; - char timestamp[50] = ""; - char cn[MAX_CN_LENGTH] = ""; - char *tz; - - int cnlen = -1; - int status = STATE_UNKNOWN; - - ASN1_STRING *tm; - int offset; - struct tm stamp; - float time_left; - int days_left; - int time_remaining; - time_t tm_t; - if (!certificate) { printf("%s\n", _("CRITICAL - No server certificate present to inspect.")); return STATE_CRITICAL; } /* Extract CN from certificate subject */ - subj = X509_get_subject_name(certificate); + X509_NAME *subj = X509_get_subject_name(certificate); if (!subj) { printf("%s\n", _("CRITICAL - Cannot retrieve certificate subject.")); return STATE_CRITICAL; } - cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); + + char cn[MAX_CN_LENGTH] = ""; + int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, cn, sizeof(cn)); if (cnlen == -1) { strcpy(cn, _("Unknown CN")); } /* Retrieve timestamp of certificate */ - tm = X509_get_notAfter(certificate); + ASN1_STRING *tm = X509_get_notAfter(certificate); + int offset = 0; + struct tm stamp = {}; /* Generate tm structure to process timestamp */ if (tm->type == V_ASN1_UTCTIME) { if (tm->length < 10) { printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); return STATE_CRITICAL; - } else { - stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0'); - if (stamp.tm_year < 50) { - stamp.tm_year += 100; - } - offset = 0; } + stamp.tm_year = (tm->data[0] - '0') * 10 + (tm->data[1] - '0'); + if (stamp.tm_year < 50) { + stamp.tm_year += 100; + } + offset = 0; + } else { if (tm->length < 12) { printf("%s\n", _("CRITICAL - Wrong time format in certificate.")); return STATE_CRITICAL; - } else { - stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + - (tm->data[2] - '0') * 10 + (tm->data[3] - '0'); - stamp.tm_year -= 1900; - offset = 2; } + stamp.tm_year = (tm->data[0] - '0') * 1000 + (tm->data[1] - '0') * 100 + + (tm->data[2] - '0') * 10 + (tm->data[3] - '0'); + stamp.tm_year -= 1900; + offset = 2; } stamp.tm_mon = (tm->data[2 + offset] - '0') * 10 + (tm->data[3 + offset] - '0') - 1; stamp.tm_mday = (tm->data[4 + offset] - '0') * 10 + (tm->data[5 + offset] - '0'); @@ -258,20 +245,25 @@ int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, stamp.tm_sec = (tm->data[10 + offset] - '0') * 10 + (tm->data[11 + offset] - '0'); stamp.tm_isdst = -1; - tm_t = timegm(&stamp); - time_left = difftime(tm_t, time(NULL)); - days_left = time_left / 86400; - tz = getenv("TZ"); + time_t tm_t = timegm(&stamp); + float time_left = difftime(tm_t, time(NULL)); + int days_left = time_left / 86400; + char *tz = getenv("TZ"); setenv("TZ", "GMT", 1); tzset(); + + char timestamp[50] = ""; strftime(timestamp, 50, "%c %z", localtime(&tm_t)); if (tz) { setenv("TZ", tz, 1); } else { unsetenv("TZ"); } + tzset(); + int time_remaining; + mp_state_enum status = STATE_UNKNOWN; if (days_left > 0 && days_left <= days_till_exp_warn) { printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", cn, days_left, timestamp); -- cgit v1.2.3-74-g34f1 From b44cbae7fb6b7bde948c0f26412e9b2a9ef8f78b Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:11:44 +0200 Subject: check_curl: less global state --- plugins/check_curl.c | 72 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7bb67645..7755d449 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -131,20 +131,6 @@ typedef struct { CURL *curl; } check_curl_global_state; -check_curl_global_state global_state = { - .curl_global_initialized = false, - .curl_easy_initialized = false, - .body_buf_initialized = false, - .body_buf = {}, - .header_buf_initialized = false, - .header_buf = {}, - .status_line_initialized = false, - .status_line = {}, - .put_buf_initialized = false, - .put_buf = {}, - .curl = NULL, -}; - static char errbuf[MAX_INPUT_BUFFER]; static char msg[DEFAULT_BUFFER_SIZE]; typedef union { @@ -166,15 +152,18 @@ static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv* static void handle_curl_option_return_code(CURLcode res, const char *option); static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list); + int redir_depth, struct curl_slist *header_list, + check_curl_global_state global_state); typedef struct { int redir_depth; check_curl_working_state working_state; int error_code; + check_curl_global_state curl_state; } redir_wrapper; static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth, check_curl_working_state working_state); + int redir_depth, check_curl_working_state working_state, + check_curl_global_state global_state); static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); @@ -246,12 +235,24 @@ int main(int argc, char **argv) { config.initial_config.server_url); } - int redir_depth = 0; + check_curl_global_state global_state = { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .body_buf_initialized = false, + .body_buf = {}, + .header_buf_initialized = false, + .header_buf = {}, + .status_line_initialized = false, + .status_line = {}, + .put_buf_initialized = false, + .put_buf = {}, + .curl = NULL, + }; check_curl_working_state working_state = config.initial_config; struct curl_slist *header_list = NULL; - exit((int)check_http(config, working_state, redir_depth, header_list)); + exit((int)check_http(config, working_state, 0, header_list, global_state)); } #ifdef HAVE_SSL @@ -403,7 +404,7 @@ int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_fam return 0; } -static void cleanup(void) { +static void cleanup(check_curl_global_state global_state) { if (global_state.status_line_initialized) { curlhelp_free_statusline(&global_state.status_line); } @@ -436,7 +437,12 @@ static void cleanup(void) { } mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list) { + int redir_depth, struct curl_slist *header_list, + check_curl_global_state global_state) { + + // ======================= + // Initialisation for curl + // ======================= /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); @@ -448,9 +454,6 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } global_state.curl_easy_initialized = true; - /* register cleanup function to shut down libcurl properly */ - atexit(cleanup); - if (verbose >= 1) { handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); @@ -651,7 +654,6 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat curl_easy_setopt(global_state.curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL - /* set SSL version, warn about insecure or unsupported versions */ if (workingState.use_ssl) { handle_curl_option_return_code( @@ -779,7 +781,6 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat "CURLOPT_SSL_CTX_FUNCTION"); } # endif - #endif /* LIBCURL_FEATURE_SSL */ /* set default or user-given user agent identification */ @@ -936,7 +937,9 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } } - /* do the request */ + // ============== + // do the request + // ============== CURLcode res = curl_easy_perform(global_state.curl); if (verbose >= 2 && workingState.http_post_data) { @@ -960,6 +963,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } + // ========== + // Evaluation + // ========== + mp_state_enum result_ssl = STATE_OK; /* certificate checks */ #ifdef LIBCURL_FEATURE_SSL @@ -1173,10 +1180,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * back here, we are in the same status as with * the libcurl method */ - redir_wrapper redir_result = - redir(&global_state.header_buf, config, redir_depth, workingState); + redir_wrapper redir_result = redir(&global_state.header_buf, config, + redir_depth, workingState, global_state); check_http(config, redir_result.working_state, redir_result.redir_depth, - header_list); + header_list, redir_result.curl_state); } } else { /* this is a specific code in the command line to @@ -1195,9 +1202,11 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat handle_curl_option_return_code( curl_easy_getinfo(global_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); + if (verbose >= 2) { printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); } + if (redir_depth > config.max_depth) { snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", config.max_depth); @@ -1365,7 +1374,8 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { } redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, - int redir_depth, check_curl_working_state working_state) { + int redir_depth, check_curl_working_state working_state, + check_curl_global_state global_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1522,7 +1532,7 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config * attached to the URL in Location */ - cleanup(); + cleanup(global_state); redir_wrapper result = { .redir_depth = redir_depth, -- cgit v1.2.3-74-g34f1 From 977e0a7f8bd3700f8514b1409205296d42216ed9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:14:29 +0200 Subject: check_curl: fix function signature --- plugins/check_curl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7755d449..0784fb8b 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -198,7 +198,8 @@ static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); #if defined(HAVE_SSL) && defined(USE_OPENSSL) -int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); +mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit); #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ static void test_file(char * /*path*/); -- cgit v1.2.3-74-g34f1 From 6969f5719268dec6459d9107e11a711dab3a18dd Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:24:16 +0200 Subject: check_curl: improve option handling a bit --- plugins/check_curl.c | 10 +++++----- plugins/check_curl.d/config.h | 13 +++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 0784fb8b..485a8744 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1139,7 +1139,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(global_state.status_line.first_line, config.server_expect)) { + if (!expected_statuscode(global_state.status_line.first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), global_state.status_line.first_line); @@ -1153,7 +1153,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } mp_state_enum result = STATE_OK; - if (config.server_expect_yn) { + if (config.server_expect.is_present) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), config.server_expect); if (verbose) { @@ -1893,9 +1893,9 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.string_expect[MAX_INPUT_BUFFER - 1] = 0; break; case 'e': /* string or substring */ - strncpy(result.config.server_expect, optarg, MAX_INPUT_BUFFER - 1); - result.config.server_expect[MAX_INPUT_BUFFER - 1] = 0; - result.config.server_expect_yn = true; + strncpy(result.config.server_expect.string, optarg, MAX_INPUT_BUFFER - 1); + result.config.server_expect.string[MAX_INPUT_BUFFER - 1] = 0; + result.config.server_expect.is_present = true; break; case 'T': /* Content-type */ result.config.http_content_type = strdup(optarg); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 25cfeda3..7566b19c 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -105,8 +105,10 @@ typedef struct { thresholds *thlds; size_t min_page_len; size_t max_page_len; - char server_expect[MAX_INPUT_BUFFER]; - bool server_expect_yn; + struct { + char string[MAX_INPUT_BUFFER]; + bool is_present; + } server_expect; char string_expect[MAX_INPUT_BUFFER]; char header_expect[MAX_INPUT_BUFFER]; mp_state_enum onredirect; @@ -154,8 +156,11 @@ check_curl_config check_curl_config_init() { .thlds = NULL, .min_page_len = 0, .max_page_len = 0, - .server_expect = HTTP_EXPECT, - .server_expect_yn = false, + .server_expect = + { + .string = HTTP_EXPECT, + .is_present = false, + }, .string_expect = "", .header_expect = "", .onredirect = STATE_OK, -- cgit v1.2.3-74-g34f1 From 99206dab7aa272e5c16c672dca81e6044ac7a4eb Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:44:55 +0200 Subject: check_curl: refactoring to modularize code --- plugins/check_curl.c | 1055 +++++++++++++++++++++-------------------- plugins/check_curl.d/config.h | 78 +-- 2 files changed, 594 insertions(+), 539 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 485a8744..f3cf7422 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -129,6 +129,9 @@ typedef struct { bool put_buf_initialized; curlhelp_read_curlbuf put_buf; CURL *curl; + + struct curl_slist *header_list; + struct curl_slist *host; } check_curl_global_state; static char errbuf[MAX_INPUT_BUFFER]; @@ -152,8 +155,7 @@ static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv* static void handle_curl_option_return_code(CURLcode res, const char *option); static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list, - check_curl_global_state global_state); + int redir_depth); typedef struct { int redir_depth; @@ -162,8 +164,7 @@ typedef struct { check_curl_global_state curl_state; } redir_wrapper; static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, - int redir_depth, check_curl_working_state working_state, - check_curl_global_state global_state); + int redir_depth, check_curl_working_state working_state); static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); @@ -172,371 +173,169 @@ static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_t static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); static char *perfd_size(size_t page_len, int /*min_page_len*/); + static void print_help(void); void print_usage(void); + static void print_curl_version(void); + static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); + static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, size_t /*datalen*/); static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, void * /*stream*/); static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); + static curlhelp_ssl_library curlhelp_get_ssl_library(void); static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); + int net_noopenssl_check_certificate(cert_ptr_union *, int, int); static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); + static char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); static mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); +static int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); -#if defined(HAVE_SSL) && defined(USE_OPENSSL) -mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, - int days_till_exp_crit); -#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ - -static void test_file(char * /*path*/); - -int main(int argc, char **argv) { - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - /* Parse extra opts if any */ - argv = np_extra_opts(&argc, argv, progname); - - /* parse arguments */ - check_curl_config_wrapper tmp_config = process_arguments(argc, argv); - if (tmp_config.errorcode == ERROR) { - usage4(_("Could not parse arguments")); - } - - const check_curl_config config = tmp_config.config; - - /* set defaults */ - if (strlen(config.user_agent) == 0) { - snprintf(config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", - progname, NP_VERSION, VERSION, curl_version()); - } - - if (config.display_html) { - printf("", - config.initial_config.use_ssl ? "https" : "http", - config.initial_config.host_name ? config.initial_config.host_name - : config.initial_config.server_address, - config.initial_config.virtualPort ? config.initial_config.virtualPort - : config.initial_config.serverPort, - config.initial_config.server_url); - } - - check_curl_global_state global_state = { - .curl_global_initialized = false, - .curl_easy_initialized = false, - .body_buf_initialized = false, - .body_buf = {}, - .header_buf_initialized = false, - .header_buf = {}, - .status_line_initialized = false, - .status_line = {}, - .put_buf_initialized = false, - .put_buf = {}, - .curl = NULL, - }; - - check_curl_working_state working_state = config.initial_config; - struct curl_slist *header_list = NULL; +// typedef struct { +// int errorcode; +// } check_curl_evaluation_wrapper; +// check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config, +// mp_check overall[static 1]) {} - exit((int)check_http(config, working_state, 0, header_list, global_state)); -} +CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm); -#ifdef HAVE_SSL -# ifdef USE_OPENSSL -int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { - (void)preverify_ok; - /* TODO: we get all certificates of the chain, so which ones - * should we test? - * TODO: is the last certificate always the server certificate? - */ - cert = X509_STORE_CTX_get_current_cert(x509_ctx); -# if OPENSSL_VERSION_NUMBER >= 0x10100000L - X509_up_ref(cert); -# endif - if (verbose >= 2) { - puts("* SSL verify callback with certificate:"); - printf("* issuer:\n"); - X509_NAME *issuer = X509_get_issuer_name(cert); - X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); - printf("* curl verify_callback:\n* subject:\n"); - X509_NAME *subject = X509_get_subject_name(cert); - X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); - puts(""); - } - return 1; -} - -CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { - (void)curl; // ignore unused parameter - (void)parm; // ignore unused parameter - if (add_sslctx_verify_fun) { - SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback); - } - - // workaround for issue: - // OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0 - // see discussion https://github.com/openssl/openssl/discussions/22690 -# ifdef SSL_OP_IGNORE_UNEXPECTED_EOF - SSL_CTX_set_options(sslctx, SSL_OP_IGNORE_UNEXPECTED_EOF); -# endif - - return CURLE_OK; -} -# endif /* USE_OPENSSL */ -#endif /* HAVE_SSL */ - -/* returns a string "HTTP/1.x" or "HTTP/2" */ -static char *string_statuscode(int major, int minor) { - static char buf[10]; - - switch (major) { - case 1: - snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); - break; - case 2: - case 3: - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - default: - /* assuming here HTTP/N with N>=4 */ - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - } - - return buf; -} - -/* Checks if the server 'reply' is one of the expected 'statuscodes' */ -static bool expected_statuscode(const char *reply, const char *statuscodes) { - char *expected; - - if ((expected = strdup(statuscodes)) == NULL) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); - } - - bool result = false; - for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { - if (strstr(reply, code) != NULL) { - result = true; - break; - } - } - - free(expected); - return result; -} - -void handle_curl_option_return_code(CURLcode res, const char *option) { - if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } -} - -int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { - struct addrinfo hints = { - .ai_family = addr_family, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_CANONNAME, +typedef struct { + int errorcode; + check_curl_global_state curl_state; +} check_curl_configure_curl_wrapper; +check_curl_configure_curl_wrapper +check_curl_configure_curl(const check_curl_static_curl_config config, + check_curl_working_state working_state, bool check_cert, + bool on_redirect_dependent, int follow_method, int max_depth) { + check_curl_configure_curl_wrapper result = { + .errorcode = OK, + .curl_state = + { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .curl = NULL, + + .body_buf_initialized = false, + .body_buf = {}, + .header_buf_initialized = false, + .header_buf = {}, + .status_line_initialized = false, + .status_line = {}, + .put_buf_initialized = false, + .put_buf = {}, + + .header_list = NULL, + .host = NULL, + }, }; - struct addrinfo *result; - int errcode = getaddrinfo(host, NULL, &hints, &result); - if (errcode != 0) { - return errcode; - } - - strcpy(buf, ""); - struct addrinfo *res = result; - - size_t buflen_remaining = buflen - 1; - size_t addrstr_len; - char addrstr[100]; - void *ptr = {0}; - while (res) { - switch (res->ai_family) { - case AF_INET: - ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; - break; - case AF_INET6: - ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - break; - } - - inet_ntop(res->ai_family, ptr, addrstr, 100); - if (verbose >= 1) { - printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, - addrstr); - } - - // Append all IPs to buf as a comma-separated string - addrstr_len = strlen(addrstr); - if (buflen_remaining > addrstr_len + 1) { - if (buf[0] != '\0') { - strncat(buf, ",", buflen_remaining); - buflen_remaining -= 1; - } - strncat(buf, addrstr, buflen_remaining); - buflen_remaining -= addrstr_len; - } - - res = res->ai_next; - } - - freeaddrinfo(result); - - return 0; -} - -static void cleanup(check_curl_global_state global_state) { - if (global_state.status_line_initialized) { - curlhelp_free_statusline(&global_state.status_line); - } - global_state.status_line_initialized = false; - - if (global_state.curl_easy_initialized) { - curl_easy_cleanup(global_state.curl); - } - global_state.curl_easy_initialized = false; - - if (global_state.curl_global_initialized) { - curl_global_cleanup(); - } - global_state.curl_global_initialized = false; - - if (global_state.body_buf_initialized) { - curlhelp_freewritebuffer(&global_state.body_buf); - } - global_state.body_buf_initialized = false; - - if (global_state.header_buf_initialized) { - curlhelp_freewritebuffer(&global_state.header_buf); - } - global_state.header_buf_initialized = false; - - if (global_state.put_buf_initialized) { - curlhelp_freereadbuffer(&global_state.put_buf); - } - global_state.put_buf_initialized = false; -} - -mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth, struct curl_slist *header_list, - check_curl_global_state global_state) { - - // ======================= - // Initialisation for curl - // ======================= - /* initialize curl */ if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); } - global_state.curl_global_initialized = true; + result.curl_state.curl_global_initialized = true; - if ((global_state.curl = curl_easy_init()) == NULL) { + if ((result.curl_state.curl = curl_easy_init()) == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); } - global_state.curl_easy_initialized = true; + result.curl_state.curl_easy_initialized = true; if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_VERBOSE, 1), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), "CURLOPT_VERBOSE"); } - /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_STDERR, stdout), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR"); if (config.automatic_decompression) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_ACCEPT_ENCODING, ""), + curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""), "CURLOPT_ACCEPT_ENCODING"); #else - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ENCODING, ""), - "CURLOPT_ENCODING"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ } /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&global_state.body_buf) < 0) { + if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); } - global_state.body_buf_initialized = true; - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_WRITEFUNCTION, curlhelp_buffer_write_callback), - "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_WRITEDATA, (void *)&global_state.body_buf), - "CURLOPT_WRITEDATA"); + result.curl_state.body_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_WRITEFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA, + (void *)&result.curl_state.body_buf), + "CURLOPT_WRITEDATA"); /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&global_state.header_buf) < 0) { + if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); } - global_state.header_buf_initialized = true; + result.curl_state.header_buf_initialized = true; - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HEADERFUNCTION, curlhelp_buffer_write_callback), - "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_WRITEHEADER, (void *)&global_state.header_buf), - "CURLOPT_WRITEHEADER"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_HEADERFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER, + (void *)&result.curl_state.header_buf), + "CURLOPT_WRITEHEADER"); /* set the error buffer */ - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_ERRORBUFFER, errbuf), - "CURLOPT_ERRORBUFFER"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf), + "CURLOPT_ERRORBUFFER"); /* set timeouts */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), "CURLOPT_CONNECTTIMEOUT"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), + curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), "CURLOPT_TIMEOUT"); /* enable haproxy protocol */ if (config.haproxy_protocol) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), + curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), "CURLOPT_HAPROXYPROTOCOL"); } // fill dns resolve cache to make curl connect to the given server_address instead of the // host_name, only required for ssl, because we use the host_name later on to make SNI happy - struct curl_slist *host = NULL; char dnscache[DEFAULT_BUFFER_SIZE]; char addrstr[DEFAULT_BUFFER_SIZE / 2]; - if (workingState.use_ssl && workingState.host_name != NULL) { + if (working_state.use_ssl && working_state.host_name != NULL) { int res; - if ((res = lookup_host(workingState.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, config.sin_family)) != 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - workingState.server_address, res, gai_strerror(res)); + working_state.server_address, res, gai_strerror(res)); die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", workingState.host_name, - workingState.serverPort, addrstr); - host = curl_slist_append(NULL, dnscache); - curl_easy_setopt(global_state.curl, CURLOPT_RESOLVE, host); + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, + working_state.serverPort, addrstr); + result.curl_state.host = curl_slist_append(NULL, dnscache); + curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); if (verbose >= 1) { printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); } @@ -544,72 +343,71 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat // If server_address is an IPv6 address it must be surround by square brackets struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, workingState.server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(workingState.server_address) + 3); + if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) { + char *new_server_address = malloc(strlen(working_state.server_address) + 3); if (new_server_address == NULL) { die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); } - snprintf(new_server_address, strlen(workingState.server_address) + 3, "[%s]", - workingState.server_address); - free(workingState.server_address); - workingState.server_address = new_server_address; + snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]", + working_state.server_address); + working_state.server_address = new_server_address; } /* compose URL: use the address we want to connect to, set Host: header later */ char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http", - (workingState.use_ssl & (workingState.host_name != NULL)) - ? workingState.host_name - : workingState.server_address, - workingState.serverPort, workingState.server_url); + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", + (working_state.use_ssl & (working_state.host_name != NULL)) + ? working_state.host_name + : working_state.server_address, + working_state.serverPort, working_state.server_url); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); } - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_URL, url), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), "CURLOPT_URL"); /* extract proxy information for legacy proxy https requests */ - if (!strcmp(workingState.http_method, "CONNECT") || - strstr(workingState.server_url, "http") == workingState.server_url) { + if (!strcmp(working_state.http_method, "CONNECT") || + strstr(working_state.server_url, "http") == working_state.server_url) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_PROXY, workingState.server_address), + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address), "CURLOPT_PROXY"); - handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_PROXYPORT, (long)workingState.serverPort), - "CURLOPT_PROXYPORT"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT, + (long)working_state.serverPort), + "CURLOPT_PROXYPORT"); if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", workingState.server_address, - workingState.serverPort); + printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address, + working_state.serverPort); } - workingState.http_method = "GET"; + working_state.http_method = "GET"; handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_URL, workingState.server_url), + curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url), "CURLOPT_URL"); } /* disable body for HEAD request */ - if (workingState.http_method && !strcmp(workingState.http_method, "HEAD")) { - workingState.no_body = true; + if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) { + working_state.no_body = true; } /* set HTTP protocol version */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), "CURLOPT_HTTP_VERSION"); /* set HTTP method */ - if (workingState.http_method) { - if (!strcmp(workingState.http_method, "POST")) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POST, 1), - "CURLOPT_POST"); - } else if (!strcmp(workingState.http_method, "PUT")) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_UPLOAD, 1), - "CURLOPT_UPLOAD"); + if (working_state.http_method) { + if (!strcmp(working_state.http_method, "POST")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); + } else if (!strcmp(working_state.http_method, "PUT")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); } else { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_CUSTOMREQUEST, - workingState.http_method), + working_state.http_method), "CURLOPT_CUSTOMREQUEST"); } } @@ -627,67 +425,71 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in * anyway */ char http_header[DEFAULT_BUFFER_SIZE]; - if (workingState.host_name != NULL && force_host_header == NULL) { - if ((workingState.virtualPort != HTTP_PORT && !workingState.use_ssl) || - (workingState.virtualPort != HTTPS_PORT && workingState.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", workingState.host_name, - workingState.virtualPort); + if (working_state.host_name != NULL && force_host_header == NULL) { + if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) || + (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name, + working_state.virtualPort); } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", workingState.host_name); + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name); } - header_list = curl_slist_append(header_list, http_header); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); } /* always close connection, be nice to servers */ snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); - header_list = curl_slist_append(header_list, http_header); + result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header); /* attach additional headers supplied by the user */ /* optionally send any other header tag */ if (config.http_opt_headers_count) { for (size_t i = 0; i < config.http_opt_headers_count; i++) { - header_list = curl_slist_append(header_list, config.http_opt_headers[i]); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]); } } /* set HTTP headers */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_HTTPHEADER, header_list), "CURLOPT_HTTPHEADER"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list), + "CURLOPT_HTTPHEADER"); #ifdef LIBCURL_FEATURE_SSL /* set SSL version, warn about insecure or unsupported versions */ - if (workingState.use_ssl) { + if (working_state.use_ssl) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSLVERSION, config.ssl_version), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version), "CURLOPT_SSLVERSION"); } /* client certificate and key to present to server (SSL) */ if (config.client_cert) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSLCERT, config.client_cert), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert), "CURLOPT_SSLCERT"); } if (config.client_privkey) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSLKEY, config.client_privkey), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey), "CURLOPT_SSLKEY"); } if (config.ca_cert) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CAINFO, config.ca_cert), "CURLOPT_CAINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert), + "CURLOPT_CAINFO"); } if (config.ca_cert || config.verify_peer_and_host) { /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks @@ -695,10 +497,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * to be less tolerant about ssl verfications */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST"); } @@ -706,7 +508,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); /* try hard to get a stack of certificates to verify against */ - if (config.check_cert) { + if (check_cert) { # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) /* inform curl to report back certificates */ switch (ssl_library) { @@ -722,14 +524,14 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* libcurl is built with OpenSSL, monitoring plugins, so falling * back to manually extracting certificate information */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); break; case CURLHELP_SSL_LIBRARY_NSS: # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) /* NSS: support for CERTINFO is implemented since 7.34.0 */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -742,7 +544,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat # if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); # else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " @@ -775,10 +577,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat # if LIBCURL_VERSION_NUM >= \ MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != + if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != CURLE_UNKNOWN_OPTION) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); } # endif @@ -786,23 +588,22 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* set default or user-given user agent identification */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_USERAGENT, config.user_agent), + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent), "CURLOPT_USERAGENT"); /* proxy-authentication */ if (strcmp(config.proxy_auth, "")) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), "CURLOPT_PROXYUSERPWD"); } /* authentication */ if (strcmp(config.user_auth, "")) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_USERPWD, config.user_auth), + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth), "CURLOPT_USERPWD"); } - /* TODO: parameter auth method, bitfield of following methods: * CURLAUTH_BASIC (default) * CURLAUTH_DIGEST @@ -820,26 +621,26 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat */ /* handle redirections */ - if (config.onredirect == STATE_DEPENDENT) { - if (config.followmethod == FOLLOW_LIBCURL) { + if (on_redirect_dependent) { + if (follow_method == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_FOLLOWLOCATION, 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION"); /* default -1 is infinite, not good, could lead to zombie plugins! Setting it to one bigger than maximal limit to handle errors nicely below */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_MAXREDIRS, config.max_depth + 1), + curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1), "CURLOPT_MAXREDIRS"); /* for now allow only http and https (we are a http(s) check plugin in the end) */ #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), "CURLOPT_REDIR_PROTOCOLS_STR"); #elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), "CURLOPT_REDIRECT_PROTOCOLS"); @@ -854,70 +655,72 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat option here is nice like for expected page size? */ } else { - /* old style redirection is handled below */ + /* old style redirection*/ } } - /* no-body */ - if (workingState.no_body) { - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_NOBODY, 1), + if (working_state.no_body) { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY"); } /* IPv4 or IPv6 forced DNS resolution */ if (config.sin_family == AF_UNSPEC) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); } else if (config.sin_family == AF_INET) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); } #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) else if (config.sin_family == AF_INET6) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); } #endif /* either send http POST data (any data, not only POST)*/ - if (!strcmp(workingState.http_method, "POST") || !strcmp(workingState.http_method, "PUT")) { + if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) { /* set content of payload for POST and PUT */ if (config.http_content_type) { snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", config.http_content_type); - header_list = curl_slist_append(header_list, http_header); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); } /* NULL indicates "HTTP Continue" in libcurl, provide an empty string * in case of no POST/PUT data */ - if (!workingState.http_post_data) { - workingState.http_post_data = ""; + if (!working_state.http_post_data) { + working_state.http_post_data = ""; } - if (!strcmp(workingState.http_method, "POST")) { + + if (!strcmp(working_state.http_method, "POST")) { /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_POSTFIELDS, - workingState.http_post_data), + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_POSTFIELDS, + working_state.http_post_data), "CURLOPT_POSTFIELDS"); - } else if (!strcmp(workingState.http_method, "PUT")) { + } else if (!strcmp(working_state.http_method, "PUT")) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_READFUNCTION, + curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&global_state.put_buf, workingState.http_post_data, - strlen(workingState.http_post_data)) < 0) { + if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data, + strlen(working_state.http_post_data)) < 0) { die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); } - global_state.put_buf_initialized = true; - - handle_curl_option_return_code(curl_easy_setopt(global_state.curl, CURLOPT_READDATA, - (void *)&global_state.put_buf), + result.curl_state.put_buf_initialized = true; + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_READDATA, + (void *)&result.curl_state.put_buf), "CURLOPT_READDATA"); handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_INFILESIZE, - (curl_off_t)strlen(workingState.http_post_data)), + curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(working_state.http_post_data)), "CURLOPT_INFILESIZE"); } } @@ -927,35 +730,267 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* enable reading cookies from a file, and if the filename is an empty string, only * enable the curl cookie engine */ handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), "CURLOPT_COOKIEFILE"); /* now enable saving cookies to a file, but only if the filename is not an empty string, * since writing it would fail */ if (*config.cookie_jar_file) { handle_curl_option_return_code( - curl_easy_setopt(global_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), "CURLOPT_COOKIEJAR"); } } + return result; +} + +#if defined(HAVE_SSL) && defined(USE_OPENSSL) +mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit); +#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ + +static void test_file(char * /*path*/); + +int main(int argc, char **argv) { + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + /* Parse extra opts if any */ + argv = np_extra_opts(&argc, argv, progname); + + /* parse arguments */ + check_curl_config_wrapper tmp_config = process_arguments(argc, argv); + if (tmp_config.errorcode == ERROR) { + usage4(_("Could not parse arguments")); + } + + const check_curl_config config = tmp_config.config; + + if (config.display_html) { + printf("", + config.initial_config.use_ssl ? "https" : "http", + config.initial_config.host_name ? config.initial_config.host_name + : config.initial_config.server_address, + config.initial_config.virtualPort ? config.initial_config.virtualPort + : config.initial_config.serverPort, + config.initial_config.server_url); + } + + check_curl_working_state working_state = config.initial_config; + + exit((int)check_http(config, working_state, 0)); +} + +#ifdef HAVE_SSL +# ifdef USE_OPENSSL +int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { + (void)preverify_ok; + /* TODO: we get all certificates of the chain, so which ones + * should we test? + * TODO: is the last certificate always the server certificate? + */ + cert = X509_STORE_CTX_get_current_cert(x509_ctx); +# if OPENSSL_VERSION_NUMBER >= 0x10100000L + X509_up_ref(cert); +# endif + if (verbose >= 2) { + puts("* SSL verify callback with certificate:"); + printf("* issuer:\n"); + X509_NAME *issuer = X509_get_issuer_name(cert); + X509_NAME_print_ex_fp(stdout, issuer, 5, XN_FLAG_MULTILINE); + printf("* curl verify_callback:\n* subject:\n"); + X509_NAME *subject = X509_get_subject_name(cert); + X509_NAME_print_ex_fp(stdout, subject, 5, XN_FLAG_MULTILINE); + puts(""); + } + return 1; +} + +CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { + (void)curl; // ignore unused parameter + (void)parm; // ignore unused parameter + if (add_sslctx_verify_fun) { + SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback); + } + + // workaround for issue: + // OpenSSL SSL_read: error:0A000126:SSL routines::unexpected eof while reading, errno 0 + // see discussion https://github.com/openssl/openssl/discussions/22690 +# ifdef SSL_OP_IGNORE_UNEXPECTED_EOF + SSL_CTX_set_options(sslctx, SSL_OP_IGNORE_UNEXPECTED_EOF); +# endif + + return CURLE_OK; +} +# endif /* USE_OPENSSL */ +#endif /* HAVE_SSL */ + +/* returns a string "HTTP/1.x" or "HTTP/2" */ +static char *string_statuscode(int major, int minor) { + static char buf[10]; + + switch (major) { + case 1: + snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); + break; + case 2: + case 3: + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + default: + /* assuming here HTTP/N with N>=4 */ + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + } + + return buf; +} + +/* Checks if the server 'reply' is one of the expected 'statuscodes' */ +static bool expected_statuscode(const char *reply, const char *statuscodes) { + char *expected; + + if ((expected = strdup(statuscodes)) == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } + + bool result = false; + for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { + if (strstr(reply, code) != NULL) { + result = true; + break; + } + } + + free(expected); + return result; +} + +void handle_curl_option_return_code(CURLcode res, const char *option) { + if (res != CURLE_OK) { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, + curl_easy_strerror(res)); + die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + } +} + +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { + struct addrinfo hints = { + .ai_family = addr_family, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + }; + + struct addrinfo *result; + int errcode = getaddrinfo(host, NULL, &hints, &result); + if (errcode != 0) { + return errcode; + } + + strcpy(buf, ""); + struct addrinfo *res = result; + + size_t buflen_remaining = buflen - 1; + size_t addrstr_len; + char addrstr[100]; + void *ptr = {0}; + while (res) { + switch (res->ai_family) { + case AF_INET: + ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + } + + inet_ntop(res->ai_family, ptr, addrstr, 100); + if (verbose >= 1) { + printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, + addrstr); + } + + // Append all IPs to buf as a comma-separated string + addrstr_len = strlen(addrstr); + if (buflen_remaining > addrstr_len + 1) { + if (buf[0] != '\0') { + strncat(buf, ",", buflen_remaining); + buflen_remaining -= 1; + } + strncat(buf, addrstr, buflen_remaining); + buflen_remaining -= addrstr_len; + } + + res = res->ai_next; + } + + freeaddrinfo(result); + + return 0; +} + +static void cleanup(check_curl_global_state global_state) { + if (global_state.status_line_initialized) { + curlhelp_free_statusline(&global_state.status_line); + } + global_state.status_line_initialized = false; + + if (global_state.curl_easy_initialized) { + curl_easy_cleanup(global_state.curl); + } + global_state.curl_easy_initialized = false; + + if (global_state.curl_global_initialized) { + curl_global_cleanup(); + } + global_state.curl_global_initialized = false; + + if (global_state.body_buf_initialized) { + curlhelp_freewritebuffer(&global_state.body_buf); + } + global_state.body_buf_initialized = false; + + if (global_state.header_buf_initialized) { + curlhelp_freewritebuffer(&global_state.header_buf); + } + global_state.header_buf_initialized = false; + + if (global_state.put_buf_initialized) { + curlhelp_freereadbuffer(&global_state.put_buf); + } + global_state.put_buf_initialized = false; + + if (global_state.header_list) { + curl_slist_free_all(global_state.header_list); + } + + if (global_state.host) { + curl_slist_free_all(global_state.host); + } +} + +mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, + int redir_depth) { + + // ======================= + // Initialisation for curl + // ======================= + check_curl_configure_curl_wrapper conf_curl_struct = check_curl_configure_curl( + config.curl_config, workingState, config.check_cert, config.on_redirect_dependent, + config.followmethod, config.max_depth); + + check_curl_global_state curl_state = conf_curl_struct.curl_state; // ============== // do the request // ============== - CURLcode res = curl_easy_perform(global_state.curl); + CURLcode res = curl_easy_perform(curl_state.curl); if (verbose >= 2 && workingState.http_post_data) { printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); } - /* free header and server IP resolve lists, we don't need it anymore */ - curl_slist_free_all(header_list); - header_list = NULL; - - if (host) { - curl_slist_free_all(host); - host = NULL; - } - /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { snprintf(msg, DEFAULT_BUFFER_SIZE, @@ -969,7 +1004,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat // ========== mp_state_enum result_ssl = STATE_OK; - /* certificate checks */ +/* certificate checks */ #ifdef LIBCURL_FEATURE_SSL if (workingState.use_ssl) { if (config.check_cert) { @@ -992,7 +1027,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat cert_ptr_union cert_ptr = {0}; cert_ptr.to_info = NULL; - res = curl_easy_getinfo(global_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); + res = curl_easy_getinfo(curl_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); if (!res && cert_ptr.to_info) { # ifdef USE_OPENSSL /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert @@ -1066,54 +1101,55 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat */ double total_time; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_TOTAL_TIME, &total_time), + curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(&global_state.header_buf, &global_state.body_buf); + size_t page_len = get_content_length(&curl_state.header_buf, &curl_state.body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_CONNECT_TIME, &time_connect), + curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); double time_appconnect; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), + curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); double time_headers; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME"); double time_firstbyte; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), + curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", - perfd_time(total_time, config.thlds, config.socket_timeout), - perfd_size(page_len, config.min_page_len), - perfd_time_connect(time_connect, config.socket_timeout), - workingState.use_ssl - ? perfd_time_ssl(time_appconnect - time_connect, config.socket_timeout) - : "", - perfd_time_headers(time_headers - time_appconnect, config.socket_timeout), - perfd_time_firstbyte(time_firstbyte - time_headers, config.socket_timeout), - perfd_time_transfer(total_time - time_firstbyte, config.socket_timeout)); + snprintf( + perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", + perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), + perfd_size(page_len, config.min_page_len), + perfd_time_connect(time_connect, config.curl_config.socket_timeout), + workingState.use_ssl + ? perfd_time_ssl(time_appconnect - time_connect, config.curl_config.socket_timeout) + : "", + perfd_time_headers(time_headers - time_appconnect, config.curl_config.socket_timeout), + perfd_time_firstbyte(time_firstbyte - time_headers, config.curl_config.socket_timeout), + perfd_time_transfer(total_time - time_firstbyte, config.curl_config.socket_timeout)); } else { snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", - perfd_time(total_time, config.thlds, config.socket_timeout), + perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), perfd_size(page_len, config.min_page_len)); } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(global_state.header_buf.buf) == 0 && strlen(global_state.body_buf.buf) == 0) { + if (strlen(curl_state.header_buf.buf) == 0 && strlen(curl_state.body_buf.buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); } /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(global_state.header_buf.buf, &global_state.status_line) < 0) { + if (curlhelp_parse_statusline(curl_state.header_buf.buf, &curl_state.status_line) < 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); @@ -1121,12 +1157,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat * line */ die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x unknown - %s", msg); } - global_state.status_line_initialized = true; + curl_state.status_line_initialized = true; /* get result code from cURL */ long httpReturnCode; handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), + curl_easy_getinfo(curl_state.curl, CURLINFO_RESPONSE_CODE, &httpReturnCode), "CURLINFO_RESPONSE_CODE"); if (verbose >= 2) { printf("* curl CURLINFO_RESPONSE_CODE is %ld\n", httpReturnCode); @@ -1134,28 +1170,28 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", global_state.header_buf.buf, - (workingState.no_body ? " [[ skipped ]]" : global_state.body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf.buf, + (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf.buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(global_state.status_line.first_line, config.server_expect.string)) { + if (!expected_statuscode(curl_state.status_line.first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - global_state.status_line.first_line); + curl_state.status_line.first_line); } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), - workingState.serverPort, global_state.status_line.first_line); + workingState.serverPort, curl_state.status_line.first_line); } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? global_state.body_buf.buf : ""); + config.show_body ? curl_state.body_buf.buf : ""); } mp_state_enum result = STATE_OK; if (config.server_expect.is_present) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), - config.server_expect); + config.server_expect.string); if (verbose) { printf("%s\n", msg); } @@ -1164,7 +1200,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* illegal return codes result in a critical state */ if (httpReturnCode >= 600 || httpReturnCode < 100) { die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - global_state.status_line.http_code, global_state.status_line.msg); + curl_state.status_line.http_code, curl_state.status_line.msg); /* server errors result in a critical state */ } else if (httpReturnCode >= 500) { result = STATE_CRITICAL; @@ -1173,25 +1209,25 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat result = STATE_WARNING; /* check redirected page if specified */ } else if (httpReturnCode >= 300) { - if (config.onredirect == STATE_DEPENDENT) { + if (config.on_redirect_dependent) { if (config.followmethod == FOLLOW_LIBCURL) { - httpReturnCode = global_state.status_line.http_code; + httpReturnCode = curl_state.status_line.http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ - redir_wrapper redir_result = redir(&global_state.header_buf, config, - redir_depth, workingState, global_state); - check_http(config, redir_result.working_state, redir_result.redir_depth, - header_list, redir_result.curl_state); + redir_wrapper redir_result = + redir(&curl_state.header_buf, config, redir_depth, workingState); + cleanup(curl_state); + check_http(config, redir_result.working_state, redir_result.redir_depth); } } else { /* this is a specific code in the command line to * be returned when a redirection is encountered */ } - result = max_state_alt(config.onredirect, result); + result = max_state_alt(config.on_redirect_result_state, result); /* all other codes are considered ok */ } else { result = STATE_OK; @@ -1201,7 +1237,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* libcurl redirection internally, handle error states here */ if (config.followmethod == FOLLOW_LIBCURL) { handle_curl_option_return_code( - curl_easy_getinfo(global_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT"); if (verbose >= 2) { @@ -1216,22 +1252,21 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* check status codes, set exit status accordingly */ - if (global_state.status_line.http_code != httpReturnCode) { + if (curl_state.status_line.http_code != httpReturnCode) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(global_state.status_line.http_major, - global_state.status_line.http_minor), - global_state.status_line.http_code, global_state.status_line.msg, httpReturnCode); + string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), + curl_state.status_line.http_code, curl_state.status_line.msg, httpReturnCode); } if (config.maximum_age >= 0) { result = max_state_alt( - check_document_dates(&global_state.header_buf, &msg, config.maximum_age), result); + check_document_dates(&curl_state.header_buf, &msg, config.maximum_age), result); } /* Page and Header content checks go here */ if (strlen(config.header_expect)) { - if (!strstr(global_state.header_buf.buf, config.header_expect)) { + if (!strstr(curl_state.header_buf.buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -1254,7 +1289,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strlen(config.string_expect)) { - if (!strstr(global_state.body_buf.buf, config.string_expect)) { + if (!strstr(curl_state.body_buf.buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -1278,7 +1313,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat if (strlen(config.regexp)) { regmatch_t pmatch[REGS]; - int errcode = regexec(&config.compiled_regex, global_state.body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&config.compiled_regex, curl_state.body_buf.buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1344,11 +1379,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat die((int)max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", state_text(result), - string_statuscode(global_state.status_line.http_major, global_state.status_line.http_minor), - global_state.status_line.http_code, global_state.status_line.msg, - strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, - (config.display_html ? "" : ""), perfstring, - (config.show_body ? global_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); + string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), + curl_state.status_line.http_code, curl_state.status_line.msg, strlen(msg) > 0 ? " - " : "", + msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, + (config.show_body ? curl_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } @@ -1375,8 +1409,7 @@ char *uri_string(const UriTextRangeA range, char *buf, size_t buflen) { } redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config config, - int redir_depth, check_curl_working_state working_state, - check_curl_global_state global_state) { + int redir_depth, check_curl_working_state working_state) { curlhelp_statusline status_line; struct phr_header headers[255]; size_t msglen; @@ -1533,8 +1566,6 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config * attached to the URL in Location */ - cleanup(global_state); - redir_wrapper result = { .redir_depth = redir_depth, .working_state = working_state, @@ -1673,7 +1704,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { if (!is_intnonneg(optarg)) { usage2(_("Timeout interval must be a positive integer"), optarg); } else { - result.config.socket_timeout = (int)strtol(optarg, NULL, 10); + result.config.curl_config.socket_timeout = (int)strtol(optarg, NULL, 10); } break; case 'c': /* critical time threshold */ @@ -1724,12 +1755,12 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case 'a': /* authorization info */ - strncpy(result.config.user_auth, optarg, MAX_INPUT_BUFFER - 1); - result.config.user_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.curl_config.user_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.curl_config.user_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'b': /* proxy-authorization info */ - strncpy(result.config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); - result.config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; + strncpy(result.config.curl_config.proxy_auth, optarg, MAX_INPUT_BUFFER - 1); + result.config.curl_config.proxy_auth[MAX_INPUT_BUFFER - 1] = 0; break; case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */ if (!result.config.initial_config.http_post_data) { @@ -1746,19 +1777,20 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.initial_config.http_method = strdup(optarg); break; case 'A': /* useragent */ - strncpy(result.config.user_agent, optarg, DEFAULT_BUFFER_SIZE); - result.config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; + strncpy(result.config.curl_config.user_agent, optarg, DEFAULT_BUFFER_SIZE); + result.config.curl_config.user_agent[DEFAULT_BUFFER_SIZE - 1] = '\0'; break; case 'k': /* Additional headers */ - if (result.config.http_opt_headers_count == 0) { - result.config.http_opt_headers = - malloc(sizeof(char *) * (++result.config.http_opt_headers_count)); + if (result.config.curl_config.http_opt_headers_count == 0) { + result.config.curl_config.http_opt_headers = + malloc(sizeof(char *) * (++result.config.curl_config.http_opt_headers_count)); } else { - result.config.http_opt_headers = - realloc(result.config.http_opt_headers, - sizeof(char *) * (++result.config.http_opt_headers_count)); + result.config.curl_config.http_opt_headers = + realloc(result.config.curl_config.http_opt_headers, + sizeof(char *) * (++result.config.curl_config.http_opt_headers_count)); } - result.config.http_opt_headers[result.config.http_opt_headers_count - 1] = optarg; + result.config.curl_config + .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ result.config.display_html = true; @@ -1805,7 +1837,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Invalid option - SSL is not available")); #endif test_file(optarg); - result.config.client_cert = optarg; + result.config.curl_config.client_cert = optarg; enable_tls = true; break; case 'K': /* use client private key */ @@ -1813,7 +1845,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Invalid option - SSL is not available")); #endif test_file(optarg); - result.config.client_privkey = optarg; + result.config.curl_config.client_privkey = optarg; enable_tls = true; break; case CA_CERT_OPTION: /* use CA chain file */ @@ -1821,14 +1853,14 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { usage4(_("Invalid option - SSL is not available")); #endif test_file(optarg); - result.config.ca_cert = optarg; + result.config.curl_config.ca_cert = optarg; enable_tls = true; break; case 'D': /* verify peer certificate & host */ #ifndef LIBCURL_FEATURE_SSL usage4(_("Invalid option - SSL is not available")); #endif - result.config.verify_peer_and_host = true; + result.config.curl_config.verify_peer_and_host = true; enable_tls = true; break; case 'S': /* use SSL */ @@ -1852,36 +1884,44 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; case 'f': /* onredirect */ if (!strcmp(optarg, "ok")) { - result.config.onredirect = STATE_OK; + result.config.on_redirect_result_state = STATE_OK; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "warning")) { - result.config.onredirect = STATE_WARNING; + result.config.on_redirect_result_state = STATE_WARNING; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "critical")) { - result.config.onredirect = STATE_CRITICAL; + result.config.on_redirect_result_state = STATE_CRITICAL; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "unknown")) { - result.config.onredirect = STATE_UNKNOWN; + result.config.on_redirect_result_state = STATE_UNKNOWN; + result.config.on_redirect_dependent = false; } else if (!strcmp(optarg, "follow")) { - result.config.onredirect = STATE_DEPENDENT; + result.config.on_redirect_dependent = true; } else if (!strcmp(optarg, "stickyport")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST | STICKY_PORT; } else if (!strcmp(optarg, "sticky")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_HOST; } else if (!strcmp(optarg, "follow")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_HTTP_CURL, result.config.followsticky = STICKY_NONE; } else if (!strcmp(optarg, "curl")) { - result.config.onredirect = STATE_DEPENDENT, + result.config.on_redirect_dependent = true; result.config.followmethod = FOLLOW_LIBCURL; } else { usage2(_("Invalid onredirect option"), optarg); } if (verbose >= 2) { - printf(_("* Following redirects set to %s\n"), - state_text(result.config.onredirect)); + if (result.config.on_redirect_dependent) { + printf(_("* Following redirects\n")); + } else { + printf(_("* Following redirects set to state %s\n"), + state_text(result.config.on_redirect_result_state)); + } } break; case 'd': /* string or substring */ @@ -1898,7 +1938,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.server_expect.is_present = true; break; case 'T': /* Content-type */ - result.config.http_content_type = strdup(optarg); + result.config.curl_config.http_content_type = strdup(optarg); break; case 'l': /* linespan */ cflags &= ~REG_NEWLINE; @@ -1933,11 +1973,11 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case '4': - result.config.sin_family = AF_INET; + result.config.curl_config.sin_family = AF_INET; break; case '6': #if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - result.config.sin_family = AF_INET6; + result.config.curl_config.sin_family = AF_INET6; #else usage4(_("IPv6 support not available")); #endif @@ -1997,30 +2037,33 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.show_body = true; break; case HTTP_VERSION_OPTION: - result.config.curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE; if (strcmp(optarg, "1.0") == 0) { - result.config.curl_http_version = CURL_HTTP_VERSION_1_0; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_0; } else if (strcmp(optarg, "1.1") == 0) { - result.config.curl_http_version = CURL_HTTP_VERSION_1_1; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_1_1; } else if ((strcmp(optarg, "2.0") == 0) || (strcmp(optarg, "2") == 0)) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) - result.config.curl_http_version = CURL_HTTP_VERSION_2_0; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_2_0; #else result.config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ + } else if ((strcmp(optarg, "3") == 0)) { + // TODO find out which libcurl version starts supporting HTTP 3 + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3; } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); exit(STATE_WARNING); } break; case AUTOMATIC_DECOMPRESSION: - result.config.automatic_decompression = true; + result.config.curl_config.automatic_decompression = true; break; case COOKIE_JAR: - result.config.cookie_jar_file = optarg; + result.config.curl_config.cookie_jar_file = optarg; break; case HAPROXY_PROTOCOL: - result.config.haproxy_protocol = true; + result.config.curl_config.haproxy_protocol = true; break; case '?': /* print short usage statement if args not parsable */ @@ -2035,7 +2078,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { /* ssl_version initialized to CURL_SSLVERSION_DEFAULT as a default. * Only set if it's non-zero. This helps when we include multiple * parameters, like -S and -C combinations */ - result.config.ssl_version = CURL_SSLVERSION_DEFAULT; + result.config.curl_config.ssl_version = CURL_SSLVERSION_DEFAULT; if (tls_option_optarg != NULL) { char *plus_ptr = strchr(optarg, '+'); if (plus_ptr) { @@ -2044,30 +2087,30 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } if (optarg[0] == '2') { - result.config.ssl_version = CURL_SSLVERSION_SSLv2; + result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv2; } else if (optarg[0] == '3') { - result.config.ssl_version = CURL_SSLVERSION_SSLv3; + result.config.curl_config.ssl_version = CURL_SSLVERSION_SSLv3; } else if (!strcmp(optarg, "1") || !strcmp(optarg, "1.0")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_0; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_0; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ } else if (!strcmp(optarg, "1.1")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_1; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_1; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ } else if (!strcmp(optarg, "1.2")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_2; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_2; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ } else if (!strcmp(optarg, "1.3")) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) - result.config.ssl_version = CURL_SSLVERSION_TLSv1_3; + result.config.curl_config.ssl_version = CURL_SSLVERSION_TLSv1_3; #else result.config.ssl_version = CURL_SSLVERSION_DEFAULT; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */ @@ -2078,35 +2121,35 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) if (got_plus) { - switch (result.config.ssl_version) { + switch (result.config.curl_config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_DEFAULT; break; } } else { - switch (result.config.ssl_version) { + switch (result.config.curl_config.ssl_version) { case CURL_SSLVERSION_TLSv1_3: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3; break; case CURL_SSLVERSION_TLSv1_2: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2; break; case CURL_SSLVERSION_TLSv1_1: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1; break; case CURL_SSLVERSION_TLSv1_0: - result.config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; + result.config.curl_config.ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0; break; } } #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */ if (verbose >= 2) { - printf(_("* Set SSL/TLS version to %d\n"), result.config.ssl_version); + printf(_("* Set SSL/TLS version to %d\n"), result.config.curl_config.ssl_version); } if (!specify_port) { result.config.initial_config.serverPort = HTTPS_PORT; @@ -2135,18 +2178,18 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); if (critical_thresholds && - result.config.thlds->critical->end > (double)result.config.socket_timeout) { - result.config.socket_timeout = (int)result.config.thlds->critical->end + 1; + result.config.thlds->critical->end > (double)result.config.curl_config.socket_timeout) { + result.config.curl_config.socket_timeout = (int)result.config.thlds->critical->end + 1; } if (verbose >= 2) { - printf("* Socket timeout set to %ld seconds\n", result.config.socket_timeout); + printf("* Socket timeout set to %ld seconds\n", result.config.curl_config.socket_timeout); } if (result.config.initial_config.http_method == NULL) { result.config.initial_config.http_method = strdup("GET"); } - if (result.config.client_cert && !result.config.client_privkey) { + if (result.config.curl_config.client_cert && !result.config.curl_config.client_privkey) { usage4(_("If you use a client certificate you must also specify a private key file")); } diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 7566b19c..be25d1bb 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -65,27 +65,32 @@ check_curl_working_state check_curl_working_state_init() { } typedef struct { - check_curl_working_state initial_config; - sa_family_t sin_family; - bool automatic_decompression; bool haproxy_protocol; + long socket_timeout; + sa_family_t sin_family; + int curl_http_version; + char **http_opt_headers; + size_t http_opt_headers_count; + int ssl_version; char *client_cert; char *client_privkey; char *ca_cert; - int ssl_version; + bool verify_peer_and_host; char user_agent[DEFAULT_BUFFER_SIZE]; - char **http_opt_headers; - size_t http_opt_headers_count; - int max_depth; - char *http_content_type; - long socket_timeout; - char user_auth[MAX_INPUT_BUFFER]; char proxy_auth[MAX_INPUT_BUFFER]; + char user_auth[MAX_INPUT_BUFFER]; + char *http_content_type; + char *cookie_jar_file; +} check_curl_static_curl_config; + +typedef struct { + check_curl_working_state initial_config; + + check_curl_static_curl_config curl_config; + int max_depth; int followmethod; int followsticky; - int curl_http_version; - char *cookie_jar_file; int maximum_age; @@ -97,7 +102,6 @@ typedef struct { mp_state_enum state_regex; bool invert_regex; - bool verify_peer_and_host; bool check_cert; bool continue_after_check_cert; int days_till_exp_warn; @@ -111,7 +115,8 @@ typedef struct { } server_expect; char string_expect[MAX_INPUT_BUFFER]; char header_expect[MAX_INPUT_BUFFER]; - mp_state_enum onredirect; + mp_state_enum on_redirect_result_state; + bool on_redirect_dependent; bool show_extended_perfdata; bool show_body; @@ -122,33 +127,35 @@ check_curl_config check_curl_config_init() { check_curl_config tmp = { .initial_config = check_curl_working_state_init(), - .sin_family = AF_UNSPEC, - - .automatic_decompression = false, - .haproxy_protocol = false, - .client_cert = NULL, - .client_privkey = NULL, - .ca_cert = NULL, - .ssl_version = CURL_SSLVERSION_DEFAULT, - .user_agent = {'\0'}, - .http_opt_headers = NULL, - .http_opt_headers_count = 0, + .curl_config = + { + .automatic_decompression = false, + .socket_timeout = DEFAULT_SOCKET_TIMEOUT, + .haproxy_protocol = false, + .sin_family = AF_UNSPEC, + .curl_http_version = CURL_HTTP_VERSION_NONE, + .http_opt_headers = NULL, + .http_opt_headers_count = 0, + .ssl_version = CURL_SSLVERSION_DEFAULT, + .client_cert = NULL, + .client_privkey = NULL, + .ca_cert = NULL, + .verify_peer_and_host = false, + .user_agent = {'\0'}, + .proxy_auth = "", + .user_auth = "", + .http_content_type = NULL, + .cookie_jar_file = NULL, + }, .max_depth = DEFAULT_MAX_REDIRS, - .http_content_type = NULL, - .socket_timeout = DEFAULT_SOCKET_TIMEOUT, - .user_auth = "", - .proxy_auth = "", .followmethod = FOLLOW_HTTP_CURL, .followsticky = STICKY_NONE, - .curl_http_version = CURL_HTTP_VERSION_NONE, - .cookie_jar_file = NULL, .maximum_age = -1, .regexp = {}, .compiled_regex = {}, .state_regex = STATE_CRITICAL, .invert_regex = false, - .verify_peer_and_host = false, .check_cert = false, .continue_after_check_cert = false, .days_till_exp_warn = 0, @@ -163,11 +170,16 @@ check_curl_config check_curl_config_init() { }, .string_expect = "", .header_expect = "", - .onredirect = STATE_OK, + .on_redirect_result_state = STATE_OK, + .on_redirect_dependent = true, .show_extended_perfdata = false, .show_body = false, .display_html = false, }; + + snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", + "check_curl", NP_VERSION, VERSION, curl_version()); + return tmp; } -- cgit v1.2.3-74-g34f1 From 684602ddec1ae3c136eec47aa078aa3fc69bbbcf Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:44:17 +0200 Subject: check_curl: http3 is only available with libcurl 7.66 or later --- plugins/check_curl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index f3cf7422..7f3bcdb1 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -2049,8 +2049,11 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { result.config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 33, 0) */ } else if ((strcmp(optarg, "3") == 0)) { - // TODO find out which libcurl version starts supporting HTTP 3 +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3; +#else + result.config.curl_http_version = CURL_HTTP_VERSION_NONE; +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */ } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); exit(STATE_WARNING); -- cgit v1.2.3-74-g34f1 From ab66b41d235c3ec38eeb8dfd2091e3b76fc1ffa3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:14:14 +0200 Subject: check_curl: create outsourced helpers in extra files --- plugins/Makefile.am | 2 + plugins/check_curl.c | 1312 ++--------------------------- plugins/check_curl.d/check_curl_helpers.c | 1217 ++++++++++++++++++++++++++ plugins/check_curl.d/check_curl_helpers.h | 125 +++ plugins/check_curl.d/config.h | 76 +- 5 files changed, 1397 insertions(+), 1335 deletions(-) create mode 100644 plugins/check_curl.d/check_curl_helpers.c create mode 100644 plugins/check_curl.d/check_curl_helpers.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 1f24d923..1a9399f0 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -66,6 +66,7 @@ EXTRA_DIST = t \ check_hpjd.d \ check_game.d \ check_radius.d \ + check_curl.d \ check_disk.d \ check_time.d \ check_users.d \ @@ -134,6 +135,7 @@ check_cluster_LDADD = $(BASEOBJS) check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLCFLAGS) $(URIPARSERCFLAGS) $(LIBCURLINCLUDE) $(URIPARSERINCLUDE) -Ipicohttpparser check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) $(URIPARSERLIBS) picohttpparser/libpicohttpparser.a +check_curl_SOURCES = check_curl.c check_curl.d/check_curl_helpers.c check_dbi_LDADD = $(NETLIBS) $(DBILIBS) check_dig_LDADD = $(NETLIBS) check_disk_LDADD = $(BASEOBJS) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7f3bcdb1..b747ec99 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -45,6 +45,7 @@ const char *email = "devel@monitoring-plugins.org"; #include "common.h" #include "utils.h" +#include "./check_curl.d/check_curl_helpers.h" #ifndef LIBCURL_PROTOCOL_HTTP # error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense @@ -53,8 +54,6 @@ const char *email = "devel@monitoring-plugins.org"; #include "curl/curl.h" #include "curl/easy.h" -#include "./picohttpparser/picohttpparser.h" - #include "uriparser/Uri.h" #include @@ -66,48 +65,10 @@ const char *email = "devel@monitoring-plugins.org"; #include -#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) - -#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN enum { MAX_IPV4_HOSTLENGTH = 255, }; -/* for buffers for header and body */ -typedef struct { - char *buf; - size_t buflen; - size_t bufsize; -} curlhelp_write_curlbuf; - -/* for buffering the data sent in PUT */ -typedef struct { - char *buf; - size_t buflen; - off_t pos; -} curlhelp_read_curlbuf; - -/* for parsing the HTTP status line */ -typedef struct { - int http_major; /* major version of the protocol, always 1 (HTTP/0.9 - * never reached the big internet most likely) */ - int http_minor; /* minor version of the protocol, usually 0 or 1 */ - int http_code; /* HTTP return code as in RFC 2145 */ - int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see - * http://support.microsoft.com/kb/318380/en-us */ - const char *msg; /* the human readable message */ - char *first_line; /* a copy of the first line */ -} curlhelp_statusline; - -/* to know the underlying SSL library used by libcurl */ -typedef enum curlhelp_ssl_library { - CURLHELP_SSL_LIBRARY_UNKNOWN, - CURLHELP_SSL_LIBRARY_OPENSSL, - CURLHELP_SSL_LIBRARY_LIBRESSL, - CURLHELP_SSL_LIBRARY_GNUTLS, - CURLHELP_SSL_LIBRARY_NSS -} curlhelp_ssl_library; - enum { REGS = 2, }; @@ -115,33 +76,12 @@ enum { #include "regex.h" // Globals -static int verbose = 0; +int verbose = 0; -typedef struct { - bool curl_global_initialized; - bool curl_easy_initialized; - bool body_buf_initialized; - curlhelp_write_curlbuf body_buf; - bool header_buf_initialized; - curlhelp_write_curlbuf header_buf; - bool status_line_initialized; - curlhelp_statusline status_line; - bool put_buf_initialized; - curlhelp_read_curlbuf put_buf; - CURL *curl; - - struct curl_slist *header_list; - struct curl_slist *host; -} check_curl_global_state; - -static char errbuf[MAX_INPUT_BUFFER]; -static char msg[DEFAULT_BUFFER_SIZE]; -typedef union { - struct curl_slist *to_info; - struct curl_certinfo *to_certinfo; -} cert_ptr_union; -static bool is_openssl_callback = false; -static bool add_sslctx_verify_fun = false; +extern char msg[DEFAULT_BUFFER_SIZE]; +extern char errbuf[MAX_INPUT_BUFFER]; +extern bool is_openssl_callback; +extern bool add_sslctx_verify_fun; #if defined(HAVE_SSL) && defined(USE_OPENSSL) static X509 *cert = NULL; @@ -153,7 +93,6 @@ typedef struct { } check_curl_config_wrapper; static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static void handle_curl_option_return_code(CURLcode res, const char *option); static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, int redir_depth); @@ -166,590 +105,22 @@ typedef struct { static redir_wrapper redir(curlhelp_write_curlbuf * /*header_buf*/, check_curl_config /*config*/, int redir_depth, check_curl_working_state working_state); -static char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); -static char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); -static char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); -static char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); -static char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); -static char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); -static char *perfd_size(size_t page_len, int /*min_page_len*/); - static void print_help(void); void print_usage(void); static void print_curl_version(void); -static int curlhelp_initwritebuffer(curlhelp_write_curlbuf * /*buf*/); -static size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, - void * /*stream*/); -static void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); - -static int curlhelp_initreadbuffer(curlhelp_read_curlbuf * /*buf*/, const char * /*data*/, - size_t /*datalen*/); -static size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, - void * /*stream*/); -static void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); - -static curlhelp_ssl_library curlhelp_get_ssl_library(void); -static const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); - -int net_noopenssl_check_certificate(cert_ptr_union *, int, int); - -static int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); -static void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); - -static char *get_header_value(const struct phr_header *headers, size_t nof_headers, - const char *header); -static mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, - char (*msg)[DEFAULT_BUFFER_SIZE], int /*maximum_age*/); -static size_t get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf); -static int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); - // typedef struct { // int errorcode; // } check_curl_evaluation_wrapper; // check_curl_evaluation_wrapper check_curl_evaluate(check_curl_config config, // mp_check overall[static 1]) {} -CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm); - -typedef struct { - int errorcode; - check_curl_global_state curl_state; -} check_curl_configure_curl_wrapper; -check_curl_configure_curl_wrapper -check_curl_configure_curl(const check_curl_static_curl_config config, - check_curl_working_state working_state, bool check_cert, - bool on_redirect_dependent, int follow_method, int max_depth) { - check_curl_configure_curl_wrapper result = { - .errorcode = OK, - .curl_state = - { - .curl_global_initialized = false, - .curl_easy_initialized = false, - .curl = NULL, - - .body_buf_initialized = false, - .body_buf = {}, - .header_buf_initialized = false, - .header_buf = {}, - .status_line_initialized = false, - .status_line = {}, - .put_buf_initialized = false, - .put_buf = {}, - - .header_list = NULL, - .host = NULL, - }, - }; - - if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { - die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); - } - result.curl_state.curl_global_initialized = true; - - if ((result.curl_state.curl = curl_easy_init()) == NULL) { - die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); - } - result.curl_state.curl_easy_initialized = true; - - if (verbose >= 1) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), - "CURLOPT_VERBOSE"); - } - /* print everything on stdout like check_http would do */ - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout), - "CURLOPT_STDERR"); - - if (config.automatic_decompression) { -#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""), - "CURLOPT_ACCEPT_ENCODING"); -#else - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); -#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ - } - - /* initialize buffer for body of the answer */ - if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) { - die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); - } - result.curl_state.body_buf_initialized = true; - - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION, - curlhelp_buffer_write_callback), - "CURLOPT_WRITEFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA, - (void *)&result.curl_state.body_buf), - "CURLOPT_WRITEDATA"); - - /* initialize buffer for header of the answer */ - if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) { - die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); - } - result.curl_state.header_buf_initialized = true; - - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION, - curlhelp_buffer_write_callback), - "CURLOPT_HEADERFUNCTION"); - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER, - (void *)&result.curl_state.header_buf), - "CURLOPT_WRITEHEADER"); - - /* set the error buffer */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf), - "CURLOPT_ERRORBUFFER"); - - /* set timeouts */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), - "CURLOPT_CONNECTTIMEOUT"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), - "CURLOPT_TIMEOUT"); - - /* enable haproxy protocol */ - if (config.haproxy_protocol) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), - "CURLOPT_HAPROXYPROTOCOL"); - } - - // fill dns resolve cache to make curl connect to the given server_address instead of the - // host_name, only required for ssl, because we use the host_name later on to make SNI happy - char dnscache[DEFAULT_BUFFER_SIZE]; - char addrstr[DEFAULT_BUFFER_SIZE / 2]; - if (working_state.use_ssl && working_state.host_name != NULL) { - int res; - if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, - config.sin_family)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - working_state.server_address, res, gai_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, - working_state.serverPort, addrstr); - result.curl_state.host = curl_slist_append(NULL, dnscache); - curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); - if (verbose >= 1) { - printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); - } - } - - // If server_address is an IPv6 address it must be surround by square brackets - struct in6_addr tmp_in_addr; - if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) { - char *new_server_address = malloc(strlen(working_state.server_address) + 3); - if (new_server_address == NULL) { - die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); - } - snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]", - working_state.server_address); - working_state.server_address = new_server_address; - } - - /* compose URL: use the address we want to connect to, set Host: header later */ - char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", - (working_state.use_ssl & (working_state.host_name != NULL)) - ? working_state.host_name - : working_state.server_address, - working_state.serverPort, working_state.server_url); - - if (verbose >= 1) { - printf("* curl CURLOPT_URL: %s\n", url); - } - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), - "CURLOPT_URL"); - - /* extract proxy information for legacy proxy https requests */ - if (!strcmp(working_state.http_method, "CONNECT") || - strstr(working_state.server_url, "http") == working_state.server_url) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address), - "CURLOPT_PROXY"); - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT, - (long)working_state.serverPort), - "CURLOPT_PROXYPORT"); - if (verbose >= 2) { - printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address, - working_state.serverPort); - } - working_state.http_method = "GET"; - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url), - "CURLOPT_URL"); - } - - /* disable body for HEAD request */ - if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) { - working_state.no_body = true; - } - - /* set HTTP protocol version */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), - "CURLOPT_HTTP_VERSION"); - - /* set HTTP method */ - if (working_state.http_method) { - if (!strcmp(working_state.http_method, "POST")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); - } else if (!strcmp(working_state.http_method, "PUT")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); - } else { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_CUSTOMREQUEST, - working_state.http_method), - "CURLOPT_CUSTOMREQUEST"); - } - } - - char *force_host_header = NULL; - /* check if Host header is explicitly set in options */ - if (config.http_opt_headers_count) { - for (size_t i = 0; i < config.http_opt_headers_count; i++) { - if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { - force_host_header = config.http_opt_headers[i]; - } - } - } - - /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in - * anyway */ - char http_header[DEFAULT_BUFFER_SIZE]; - if (working_state.host_name != NULL && force_host_header == NULL) { - if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) || - (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name, - working_state.virtualPort); - } else { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name); - } - result.curl_state.header_list = - curl_slist_append(result.curl_state.header_list, http_header); - } - - /* always close connection, be nice to servers */ - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); - result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header); - - /* attach additional headers supplied by the user */ - /* optionally send any other header tag */ - if (config.http_opt_headers_count) { - for (size_t i = 0; i < config.http_opt_headers_count; i++) { - result.curl_state.header_list = - curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]); - } - } - - /* set HTTP headers */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list), - "CURLOPT_HTTPHEADER"); - -#ifdef LIBCURL_FEATURE_SSL - /* set SSL version, warn about insecure or unsupported versions */ - if (working_state.use_ssl) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version), - "CURLOPT_SSLVERSION"); - } - - /* client certificate and key to present to server (SSL) */ - if (config.client_cert) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert), - "CURLOPT_SSLCERT"); - } - - if (config.client_privkey) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey), - "CURLOPT_SSLKEY"); - } - - if (config.ca_cert) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert), - "CURLOPT_CAINFO"); - } - - if (config.ca_cert || config.verify_peer_and_host) { - /* per default if we have a CA verify both the peer and the - * hostname in the certificate, can be switched off later */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), - "CURLOPT_SSL_VERIFYHOST"); - } else { - /* backward-compatible behaviour, be tolerant in checks - * TODO: depending on more options have aspects we want - * to be less tolerant about ssl verfications - */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), - "CURLOPT_SSL_VERIFYPEER"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), - "CURLOPT_SSL_VERIFYHOST"); - } - - /* detect SSL library used by libcurl */ - curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); - - /* try hard to get a stack of certificates to verify against */ - if (check_cert) { -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) - /* inform curl to report back certificates */ - switch (ssl_library) { - case CURLHELP_SSL_LIBRARY_OPENSSL: - case CURLHELP_SSL_LIBRARY_LIBRESSL: - /* set callback to extract certificate with OpenSSL context function (works with - * OpenSSL-style libraries only!) */ -# ifdef USE_OPENSSL - /* libcurl and monitoring plugins built with OpenSSL, good */ - add_sslctx_verify_fun = true; - is_openssl_callback = true; -# endif /* USE_OPENSSL */ - /* libcurl is built with OpenSSL, monitoring plugins, so falling - * back to manually extracting certificate information */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); - break; - - case CURLHELP_SSL_LIBRARY_NSS: -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) - /* NSS: support for CERTINFO is implemented since 7.34.0 */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); -# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " - "'%s' is too old)\n", - curlhelp_get_ssl_library_string(ssl_library)); -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ - break; - - case CURLHELP_SSL_LIBRARY_GNUTLS: -# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) - /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); -# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " - "'%s' is too old)\n", - curlhelp_get_ssl_library_string(ssl_library)); -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ - break; - - case CURLHELP_SSL_LIBRARY_UNKNOWN: - default: - die(STATE_CRITICAL, - "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must " - "implement first)\n", - curlhelp_get_ssl_library_string(ssl_library)); - break; - } -# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ - /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ - if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || - ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { - add_sslctx_verify_fun = true; - } else { - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no " - "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " - "too old and has no CURLOPT_CERTINFO)\n"); - } -# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ - } - -# if LIBCURL_VERSION_NUM >= \ - MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ - // ssl ctx function is not available with all ssl backends - if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != - CURLE_UNKNOWN_OPTION) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), - "CURLOPT_SSL_CTX_FUNCTION"); - } -# endif -#endif /* LIBCURL_FEATURE_SSL */ - - /* set default or user-given user agent identification */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent), - "CURLOPT_USERAGENT"); - - /* proxy-authentication */ - if (strcmp(config.proxy_auth, "")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), - "CURLOPT_PROXYUSERPWD"); - } - - /* authentication */ - if (strcmp(config.user_auth, "")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth), - "CURLOPT_USERPWD"); - } - /* TODO: parameter auth method, bitfield of following methods: - * CURLAUTH_BASIC (default) - * CURLAUTH_DIGEST - * CURLAUTH_DIGEST_IE - * CURLAUTH_NEGOTIATE - * CURLAUTH_NTLM - * CURLAUTH_NTLM_WB - * - * convenience tokens for typical sets of methods: - * CURLAUTH_ANYSAFE: most secure, without BASIC - * or CURLAUTH_ANY: most secure, even BASIC if necessary - * - * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, - * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); - */ - - /* handle redirections */ - if (on_redirect_dependent) { - if (follow_method == FOLLOW_LIBCURL) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), - "CURLOPT_FOLLOWLOCATION"); - - /* default -1 is infinite, not good, could lead to zombie plugins! - Setting it to one bigger than maximal limit to handle errors nicely below - */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1), - "CURLOPT_MAXREDIRS"); - - /* for now allow only http and https (we are a http(s) check plugin in the end) */ -#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), - "CURLOPT_REDIR_PROTOCOLS_STR"); -#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_REDIR_PROTOCOLS, - CURLPROTO_HTTP | CURLPROTO_HTTPS), - "CURLOPT_REDIRECT_PROTOCOLS"); -#endif - - /* TODO: handle the following aspects of redirection, make them - * command line options too later: - CURLOPT_POSTREDIR: method switch - CURLINFO_REDIRECT_URL: custom redirect option - CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols - CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range - option here is nice like for expected page size? - */ - } else { - /* old style redirection*/ - } - } - /* no-body */ - if (working_state.no_body) { - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), - "CURLOPT_NOBODY"); - } - - /* IPv4 or IPv6 forced DNS resolution */ - if (config.sin_family == AF_UNSPEC) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); - } else if (config.sin_family == AF_INET) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); - } -#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) - else if (config.sin_family == AF_INET6) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), - "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); - } -#endif - - /* either send http POST data (any data, not only POST)*/ - if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) { - /* set content of payload for POST and PUT */ - if (config.http_content_type) { - snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", - config.http_content_type); - result.curl_state.header_list = - curl_slist_append(result.curl_state.header_list, http_header); - } - /* NULL indicates "HTTP Continue" in libcurl, provide an empty string - * in case of no POST/PUT data */ - if (!working_state.http_post_data) { - working_state.http_post_data = ""; - } - - if (!strcmp(working_state.http_method, "POST")) { - /* POST method, set payload with CURLOPT_POSTFIELDS */ - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_POSTFIELDS, - working_state.http_post_data), - "CURLOPT_POSTFIELDS"); - } else if (!strcmp(working_state.http_method, "PUT")) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION, - (curl_read_callback)curlhelp_buffer_read_callback), - "CURLOPT_READFUNCTION"); - if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data, - strlen(working_state.http_post_data)) < 0) { - die(STATE_UNKNOWN, - "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); - } - result.curl_state.put_buf_initialized = true; - handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, - CURLOPT_READDATA, - (void *)&result.curl_state.put_buf), - "CURLOPT_READDATA"); - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, - (curl_off_t)strlen(working_state.http_post_data)), - "CURLOPT_INFILESIZE"); - } - } - - /* cookie handling */ - if (config.cookie_jar_file != NULL) { - /* enable reading cookies from a file, and if the filename is an empty string, only - * enable the curl cookie engine */ - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), - "CURLOPT_COOKIEFILE"); - /* now enable saving cookies to a file, but only if the filename is not an empty string, - * since writing it would fail */ - if (*config.cookie_jar_file) { - handle_curl_option_return_code( - curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), - "CURLOPT_COOKIEJAR"); - } - } - return result; -} - #if defined(HAVE_SSL) && defined(USE_OPENSSL) mp_state_enum np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); #endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */ -static void test_file(char * /*path*/); - int main(int argc, char **argv) { setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -805,7 +176,11 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { } return 1; } +# endif /* USE_OPENSSL */ +#endif /* HAVE_SSL */ +#ifdef HAVE_SSL +# ifdef USE_OPENSSL CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { (void)curl; // ignore unused parameter (void)parm; // ignore unused parameter @@ -825,151 +200,6 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { # endif /* USE_OPENSSL */ #endif /* HAVE_SSL */ -/* returns a string "HTTP/1.x" or "HTTP/2" */ -static char *string_statuscode(int major, int minor) { - static char buf[10]; - - switch (major) { - case 1: - snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); - break; - case 2: - case 3: - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - default: - /* assuming here HTTP/N with N>=4 */ - snprintf(buf, sizeof(buf), "HTTP/%d", major); - break; - } - - return buf; -} - -/* Checks if the server 'reply' is one of the expected 'statuscodes' */ -static bool expected_statuscode(const char *reply, const char *statuscodes) { - char *expected; - - if ((expected = strdup(statuscodes)) == NULL) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); - } - - bool result = false; - for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { - if (strstr(reply, code) != NULL) { - result = true; - break; - } - } - - free(expected); - return result; -} - -void handle_curl_option_return_code(CURLcode res, const char *option) { - if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } -} - -int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { - struct addrinfo hints = { - .ai_family = addr_family, - .ai_socktype = SOCK_STREAM, - .ai_flags = AI_CANONNAME, - }; - - struct addrinfo *result; - int errcode = getaddrinfo(host, NULL, &hints, &result); - if (errcode != 0) { - return errcode; - } - - strcpy(buf, ""); - struct addrinfo *res = result; - - size_t buflen_remaining = buflen - 1; - size_t addrstr_len; - char addrstr[100]; - void *ptr = {0}; - while (res) { - switch (res->ai_family) { - case AF_INET: - ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; - break; - case AF_INET6: - ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - break; - } - - inet_ntop(res->ai_family, ptr, addrstr, 100); - if (verbose >= 1) { - printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, - addrstr); - } - - // Append all IPs to buf as a comma-separated string - addrstr_len = strlen(addrstr); - if (buflen_remaining > addrstr_len + 1) { - if (buf[0] != '\0') { - strncat(buf, ",", buflen_remaining); - buflen_remaining -= 1; - } - strncat(buf, addrstr, buflen_remaining); - buflen_remaining -= addrstr_len; - } - - res = res->ai_next; - } - - freeaddrinfo(result); - - return 0; -} - -static void cleanup(check_curl_global_state global_state) { - if (global_state.status_line_initialized) { - curlhelp_free_statusline(&global_state.status_line); - } - global_state.status_line_initialized = false; - - if (global_state.curl_easy_initialized) { - curl_easy_cleanup(global_state.curl); - } - global_state.curl_easy_initialized = false; - - if (global_state.curl_global_initialized) { - curl_global_cleanup(); - } - global_state.curl_global_initialized = false; - - if (global_state.body_buf_initialized) { - curlhelp_freewritebuffer(&global_state.body_buf); - } - global_state.body_buf_initialized = false; - - if (global_state.header_buf_initialized) { - curlhelp_freewritebuffer(&global_state.header_buf); - } - global_state.header_buf_initialized = false; - - if (global_state.put_buf_initialized) { - curlhelp_freereadbuffer(&global_state.put_buf); - } - global_state.put_buf_initialized = false; - - if (global_state.header_list) { - curl_slist_free_all(global_state.header_list); - } - - if (global_state.host) { - curl_slist_free_all(global_state.host); - } -} - mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, int redir_depth) { @@ -981,6 +211,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat config.followmethod, config.max_depth); check_curl_global_state curl_state = conf_curl_struct.curl_state; + workingState = conf_curl_struct.working_state; // ============== // do the request @@ -1103,7 +334,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(&curl_state.header_buf, &curl_state.body_buf); + size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); char perfstring[DEFAULT_BUFFER_SIZE]; if (config.show_extended_perfdata) { double time_connect; @@ -1144,12 +375,12 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* return a CRITICAL status if we couldn't read any data */ - if (strlen(curl_state.header_buf.buf) == 0 && strlen(curl_state.body_buf.buf) == 0) { + if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) { die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); } /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(curl_state.header_buf.buf, &curl_state.status_line) < 0) { + if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, "Unparsable status line in %.3g seconds response time|%s\n", total_time, perfstring); @@ -1170,22 +401,22 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* print status line, header, body if verbose */ if (verbose >= 2) { - printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf.buf, - (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf.buf)); + printf("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", curl_state.header_buf->buf, + (workingState.no_body ? " [[ skipped ]]" : curl_state.body_buf->buf)); } /* make sure the status line matches the response we are looking for */ - if (!expected_statuscode(curl_state.status_line.first_line, config.server_expect.string)) { + if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - curl_state.status_line.first_line); + curl_state.status_line->first_line); } else { snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), - workingState.serverPort, curl_state.status_line.first_line); + workingState.serverPort, curl_state.status_line->first_line); } die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? curl_state.body_buf.buf : ""); + config.show_body ? curl_state.body_buf->buf : ""); } mp_state_enum result = STATE_OK; @@ -1200,7 +431,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat /* illegal return codes result in a critical state */ if (httpReturnCode >= 600 || httpReturnCode < 100) { die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - curl_state.status_line.http_code, curl_state.status_line.msg); + curl_state.status_line->http_code, curl_state.status_line->msg); /* server errors result in a critical state */ } else if (httpReturnCode >= 500) { result = STATE_CRITICAL; @@ -1211,14 +442,14 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } else if (httpReturnCode >= 300) { if (config.on_redirect_dependent) { if (config.followmethod == FOLLOW_LIBCURL) { - httpReturnCode = curl_state.status_line.http_code; + httpReturnCode = curl_state.status_line->http_code; } else { /* old check_http style redirection, if we come * back here, we are in the same status as with * the libcurl method */ redir_wrapper redir_result = - redir(&curl_state.header_buf, config, redir_depth, workingState); + redir(curl_state.header_buf, config, redir_depth, workingState); cleanup(curl_state); check_http(config, redir_result.working_state, redir_result.redir_depth); } @@ -1252,21 +483,21 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* check status codes, set exit status accordingly */ - if (curl_state.status_line.http_code != httpReturnCode) { + if (curl_state.status_line->http_code != httpReturnCode) { die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), - curl_state.status_line.http_code, curl_state.status_line.msg, httpReturnCode); + string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); } if (config.maximum_age >= 0) { result = max_state_alt( - check_document_dates(&curl_state.header_buf, &msg, config.maximum_age), result); + check_document_dates(curl_state.header_buf, msg, config.maximum_age), result); } /* Page and Header content checks go here */ if (strlen(config.header_expect)) { - if (!strstr(curl_state.header_buf.buf, config.header_expect)) { + if (!strstr(curl_state.header_buf->buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -1289,7 +520,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } if (strlen(config.string_expect)) { - if (!strstr(curl_state.body_buf.buf, config.string_expect)) { + if (!strstr(curl_state.body_buf->buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -1313,7 +544,7 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat if (strlen(config.regexp)) { regmatch_t pmatch[REGS]; - int errcode = regexec(&config.compiled_regex, curl_state.body_buf.buf, REGS, pmatch, 0); + int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0); if ((errcode == 0 && !config.invert_regex) || (errcode == REG_NOMATCH && config.invert_regex)) { /* OK - No-op to avoid changing the logic around it */ @@ -1379,10 +610,10 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat die((int)max_state_alt(result, result_ssl), "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", state_text(result), - string_statuscode(curl_state.status_line.http_major, curl_state.status_line.http_minor), - curl_state.status_line.http_code, curl_state.status_line.msg, strlen(msg) > 0 ? " - " : "", + string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, strlen(msg) > 0 ? " - " : "", msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, - (config.show_body ? curl_state.body_buf.buf : ""), (config.show_body ? "\n" : "")); + (config.show_body ? curl_state.body_buf->buf : ""), (config.show_body ? "\n" : "")); return max_state_alt(result, result_ssl); } @@ -1574,14 +805,6 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config return result; } -/* check whether a file exists */ -void test_file(char *path) { - if (access(path, R_OK) == 0) { - return; - } - usage2(_("file does not exist or is not readable"), path); -} - check_curl_config_wrapper process_arguments(int argc, char **argv) { enum { INVERT_REGEX = CHAR_MAX + 1, @@ -2212,42 +1435,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { return result; } -char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), - thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), - thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); -} - -char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { - return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} - -char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { - return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} - -char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { - return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} - -char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { - return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} - -char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { - return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} - -char *perfd_size(size_t page_len, int min_page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, - true, 0, false, 0); -} - void print_help(void) { print_revision(progname, NP_VERSION); @@ -2523,403 +1710,6 @@ void print_usage(void) { void print_curl_version(void) { printf("%s\n", curl_version()); } -int curlhelp_initwritebuffer(curlhelp_write_curlbuf *buf) { - buf->bufsize = DEFAULT_BUFFER_SIZE; - buf->buflen = 0; - buf->buf = (char *)malloc(buf->bufsize); - if (buf->buf == NULL) { - return -1; - } - return 0; -} - -size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) { - curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; - - while (buf->bufsize < buf->buflen + size * nmemb + 1) { - buf->bufsize = buf->bufsize * 2; - buf->buf = (char *)realloc(buf->buf, buf->bufsize); - if (buf->buf == NULL) { - fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); - return 0; - } - } - - memcpy(buf->buf + buf->buflen, buffer, size * nmemb); - buf->buflen += size * nmemb; - buf->buf[buf->buflen] = '\0'; - - return size * nmemb; -} - -size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { - curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; - - size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos); - - memcpy(buffer, buf->buf + buf->pos, minimalSize); - buf->pos += minimalSize; - - return minimalSize; -} - -void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { - free(buf->buf); - buf->buf = NULL; -} - -int curlhelp_initreadbuffer(curlhelp_read_curlbuf *buf, const char *data, size_t datalen) { - buf->buflen = datalen; - buf->buf = (char *)malloc(buf->buflen); - if (buf->buf == NULL) { - return -1; - } - memcpy(buf->buf, data, datalen); - buf->pos = 0; - return 0; -} - -void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { - free(buf->buf); - buf->buf = NULL; -} - -/* TODO: where to put this, it's actually part of sstrings2 (logically)? - */ -const char *strrstr2(const char *haystack, const char *needle) { - if (haystack == NULL || needle == NULL) { - return NULL; - } - - if (haystack[0] == '\0' || needle[0] == '\0') { - return NULL; - } - - int counter = 0; - const char *prev_pos = NULL; - const char *pos = haystack; - size_t len = strlen(needle); - for (;;) { - pos = strstr(pos, needle); - if (pos == NULL) { - if (counter == 0) { - return NULL; - } - return prev_pos; - } - counter++; - prev_pos = pos; - pos += len; - if (*pos == '\0') { - return prev_pos; - } - } -} - -int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { - /* find last start of a new header */ - const char *start = strrstr2(buf, "\r\nHTTP/"); - if (start != NULL) { - start += 2; - buf = start; - } - - char *first_line_end = strstr(buf, "\r\n"); - if (first_line_end == NULL) { - return -1; - } - - size_t first_line_len = (size_t)(first_line_end - buf); - status_line->first_line = (char *)malloc(first_line_len + 1); - if (status_line->first_line == NULL) { - return -1; - } - memcpy(status_line->first_line, buf, first_line_len); - status_line->first_line[first_line_len] = '\0'; - char *first_line_buf = strdup(status_line->first_line); - - /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ - char *temp_string = strtok(first_line_buf, "/"); - if (temp_string == NULL) { - free(first_line_buf); - return -1; - } - if (strcmp(temp_string, "HTTP") != 0) { - free(first_line_buf); - return -1; - } - - temp_string = strtok(NULL, " "); - if (temp_string == NULL) { - free(first_line_buf); - return -1; - } - - char *temp_string_2; - if (strchr(temp_string, '.') != NULL) { - - /* HTTP 1.x case */ - strtok(temp_string, "."); - status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - strtok(NULL, " "); - status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - temp_string += 4; /* 1.x SP */ - } else { - /* HTTP 2 case */ - status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); - status_line->http_minor = 0; - temp_string += 2; /* 2 SP */ - } - - /* status code: "404" or "404.1", then SP */ - temp_string = strtok(temp_string, " "); - if (temp_string == NULL) { - free(first_line_buf); - return -1; - } - if (strchr(temp_string, '.') != NULL) { - char *ppp; - ppp = strtok(temp_string, "."); - status_line->http_code = (int)strtol(ppp, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - ppp = strtok(NULL, ""); - status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10); - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - temp_string += 6; /* 400.1 SP */ - } else { - status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10); - status_line->http_subcode = -1; - if (*temp_string_2 != '\0') { - free(first_line_buf); - return -1; - } - temp_string += 4; /* 400 SP */ - } - - /* Human readable message: "Not Found" CRLF */ - - temp_string = strtok(temp_string, ""); - if (temp_string == NULL) { - status_line->msg = ""; - return 0; - } - status_line->msg = status_line->first_line + (temp_string - first_line_buf); - free(first_line_buf); - - return 0; -} - -void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } - -char *get_header_value(const struct phr_header *headers, const size_t nof_headers, - const char *header) { - for (size_t i = 0; i < nof_headers; i++) { - if (headers[i].name != NULL && - strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { - return strndup(headers[i].value, headers[i].value_len); - } - } - return NULL; -} - -mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, - char (*msg)[DEFAULT_BUFFER_SIZE], int maximum_age) { - struct phr_header headers[255]; - size_t nof_headers = 255; - curlhelp_statusline status_line; - size_t msglen; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, - &status_line.http_minor, &status_line.http_code, &status_line.msg, - &msglen, headers, &nof_headers, 0); - - if (res == -1) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); - } - - char *server_date = get_header_value(headers, nof_headers, "date"); - char *document_date = get_header_value(headers, nof_headers, "last-modified"); - - mp_state_enum date_result = STATE_OK; - if (!server_date || !*server_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_UNKNOWN, date_result); - - } else if (!document_date || !*document_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - - } else { - time_t srv_data = curl_getdate(server_date, NULL); - time_t doc_data = curl_getdate(document_date, NULL); - if (verbose >= 2) { - printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, - document_date, (int)doc_data); - } - if (srv_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, - server_date); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, - document_date); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data > srv_data + 30) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, - (int)doc_data - (int)srv_data); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else if (doc_data < srv_data - maximum_age) { - time_t last_modified = (srv_data - doc_data); - if (last_modified > (60 * 60 * 24 * 2)) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, - ((float)last_modified) / (60 * 60 * 24)); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } else { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), *msg, - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); - strcpy(*msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - } - } - } - - if (server_date) { - free(server_date); - } - if (document_date) { - free(document_date); - } - - return date_result; -} - -size_t get_content_length(const curlhelp_write_curlbuf *header_buf, - const curlhelp_write_curlbuf *body_buf) { - struct phr_header headers[255]; - size_t nof_headers = 255; - size_t msglen; - curlhelp_statusline status_line; - int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, - &status_line.http_minor, &status_line.http_code, &status_line.msg, - &msglen, headers, &nof_headers, 0); - - if (res == -1) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); - } - - char *content_length_s = get_header_value(headers, nof_headers, "content-length"); - if (!content_length_s) { - return header_buf->buflen + body_buf->buflen; - } - - content_length_s += strspn(content_length_s, " \t"); - size_t content_length = atoi(content_length_s); - if (content_length != body_buf->buflen) { - /* TODO: should we warn if the actual and the reported body length don't match? */ - } - - if (content_length_s) { - free(content_length_s); - } - - return header_buf->buflen + body_buf->buflen; -} - -/* TODO: is there a better way in libcurl to check for the SSL library? */ -curlhelp_ssl_library curlhelp_get_ssl_library(void) { - curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; - - curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW); - if (version_data == NULL) { - return CURLHELP_SSL_LIBRARY_UNKNOWN; - } - - char *ssl_version = strdup(version_data->ssl_version); - if (ssl_version == NULL) { - return CURLHELP_SSL_LIBRARY_UNKNOWN; - } - - char *library = strtok(ssl_version, "/"); - if (library == NULL) { - return CURLHELP_SSL_LIBRARY_UNKNOWN; - } - - if (strcmp(library, "OpenSSL") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; - } else if (strcmp(library, "LibreSSL") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; - } else if (strcmp(library, "GnuTLS") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; - } else if (strcmp(library, "NSS") == 0) { - ssl_library = CURLHELP_SSL_LIBRARY_NSS; - } - - if (verbose >= 2) { - printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, - ssl_library); - } - - free(ssl_version); - - return ssl_library; -} - -const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library ssl_library) { - switch (ssl_library) { - case CURLHELP_SSL_LIBRARY_OPENSSL: - return "OpenSSL"; - case CURLHELP_SSL_LIBRARY_LIBRESSL: - return "LibreSSL"; - case CURLHELP_SSL_LIBRARY_GNUTLS: - return "GnuTLS"; - case CURLHELP_SSL_LIBRARY_NSS: - return "NSS"; - case CURLHELP_SSL_LIBRARY_UNKNOWN: - default: - return "unknown"; - } -} - #ifdef LIBCURL_FEATURE_SSL # ifndef USE_OPENSSL time_t parse_cert_date(const char *s) { @@ -2938,35 +1728,31 @@ time_t parse_cert_date(const char *s) { return date; } +# endif /* USE_OPENSSL */ +#endif /* LIBCURL_FEATURE_SSL */ +#ifdef LIBCURL_FEATURE_SSL +# ifndef USE_OPENSSL /* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to * OpenSSL could be this function */ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_warn, int days_till_exp_crit) { - struct curl_slist *slist; - int cname_found = 0; - char *start_date_str = NULL; - char *end_date_str = NULL; - time_t start_date; - time_t end_date; - char *tz; - float time_left; - int days_left; - int time_remaining; - char timestamp[50] = ""; - int status = STATE_UNKNOWN; if (verbose >= 2) { printf("**** REQUEST CERTIFICATES ****\n"); } + char *start_date_str = NULL; + char *end_date_str = NULL; bool have_first_cert = false; + bool cname_found = false; for (int i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) { if (have_first_cert) { break; } + struct curl_slist *slist; for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) { /* find first common name in subject, * TODO: check alternative subjects for @@ -2982,7 +1768,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } if (p != NULL) { if (strncmp(host_name, p + d, strlen(host_name)) == 0) { - cname_found = 1; + cname_found = true; } } } else if (strncasecmp(slist->data, "Start Date:", 11) == 0) { @@ -3008,7 +1794,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ return STATE_CRITICAL; } - start_date = parse_cert_date(start_date_str); + time_t start_date = parse_cert_date(start_date_str); if (start_date <= 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"), start_date_str); @@ -3016,7 +1802,7 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ return STATE_WARNING; } - end_date = parse_cert_date(end_date_str); + time_t end_date = parse_cert_date(end_date_str); if (end_date <= 0) { snprintf(msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"), start_date_str); @@ -3024,11 +1810,13 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ return STATE_WARNING; } - time_left = difftime(end_date, time(NULL)); - days_left = time_left / 86400; - tz = getenv("TZ"); + float time_left = difftime(end_date, time(NULL)); + int days_left = time_left / 86400; + char *tz = getenv("TZ"); setenv("TZ", "GMT", 1); tzset(); + + char timestamp[50] = ""; strftime(timestamp, 50, "%c %z", localtime(&end_date)); if (tz) { setenv("TZ", tz, 1); @@ -3037,6 +1825,8 @@ int net_noopenssl_check_certificate(cert_ptr_union *cert_ptr, int days_till_exp_ } tzset(); + mp_state_enum status = STATE_UNKNOWN; + int time_remaining; if (days_left > 0 && days_left <= days_till_exp_warn) { printf(_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left > days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, days_left, diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c new file mode 100644 index 00000000..42d63bc4 --- /dev/null +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -0,0 +1,1217 @@ +#include "./check_curl_helpers.h" +#include +#include +#include +#include +#include +#include "../utils.h" + +extern int verbose; +char msg[DEFAULT_BUFFER_SIZE]; +char errbuf[MAX_INPUT_BUFFER]; +bool is_openssl_callback = false; +bool add_sslctx_verify_fun = false; + +check_curl_configure_curl_wrapper +check_curl_configure_curl(const check_curl_static_curl_config config, + check_curl_working_state working_state, bool check_cert, + bool on_redirect_dependent, int follow_method, int max_depth) { + check_curl_configure_curl_wrapper result = { + .errorcode = OK, + .curl_state = + { + .curl_global_initialized = false, + .curl_easy_initialized = false, + .curl = NULL, + + .body_buf_initialized = false, + .body_buf = NULL, + .header_buf_initialized = false, + .header_buf = NULL, + .status_line_initialized = false, + .status_line = NULL, + .put_buf_initialized = false, + .put_buf = NULL, + + .header_list = NULL, + .host = NULL, + }, + }; + + if ((result.curl_state.status_line = calloc(1, sizeof(curlhelp_statusline))) == NULL) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - allocation of statusline failed\n"); + } + + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n"); + } + result.curl_state.curl_global_initialized = true; + + if ((result.curl_state.curl = curl_easy_init()) == NULL) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n"); + } + result.curl_state.curl_easy_initialized = true; + + if (verbose >= 1) { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_VERBOSE, 1), + "CURLOPT_VERBOSE"); + } + + /* print everything on stdout like check_http would do */ + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_STDERR, stdout), + "CURLOPT_STDERR"); + + if (config.automatic_decompression) { +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ACCEPT_ENCODING, ""), + "CURLOPT_ACCEPT_ENCODING"); +#else + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ENCODING, ""), "CURLOPT_ENCODING"); +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 6) */ + } + + /* initialize buffer for body of the answer */ + if (curlhelp_initwritebuffer(&result.curl_state.body_buf) < 0) { + die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n"); + } + result.curl_state.body_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_WRITEFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEDATA, + (void *)result.curl_state.body_buf), + "CURLOPT_WRITEDATA"); + + /* initialize buffer for header of the answer */ + if (curlhelp_initwritebuffer(&result.curl_state.header_buf) < 0) { + die(STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n"); + } + result.curl_state.header_buf_initialized = true; + + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_HEADERFUNCTION, + curlhelp_buffer_write_callback), + "CURLOPT_HEADERFUNCTION"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_WRITEHEADER, + (void *)result.curl_state.header_buf), + "CURLOPT_WRITEHEADER"); + + /* set the error buffer */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_ERRORBUFFER, errbuf), + "CURLOPT_ERRORBUFFER"); + + /* set timeouts */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CONNECTTIMEOUT, config.socket_timeout), + "CURLOPT_CONNECTTIMEOUT"); + + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_TIMEOUT, config.socket_timeout), + "CURLOPT_TIMEOUT"); + + /* enable haproxy protocol */ + if (config.haproxy_protocol) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_HAPROXYPROTOCOL, 1L), + "CURLOPT_HAPROXYPROTOCOL"); + } + + // fill dns resolve cache to make curl connect to the given server_address instead of the + // host_name, only required for ssl, because we use the host_name later on to make SNI happy + char dnscache[DEFAULT_BUFFER_SIZE]; + char addrstr[DEFAULT_BUFFER_SIZE / 2]; + if (working_state.use_ssl && working_state.host_name != NULL) { + int res; + if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, + config.sin_family)) != 0) { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + working_state.server_address, res, gai_strerror(res)); + die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + } + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, + working_state.serverPort, addrstr); + result.curl_state.host = curl_slist_append(NULL, dnscache); + curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); + if (verbose >= 1) { + printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); + } + } + + // If server_address is an IPv6 address it must be surround by square brackets + struct in6_addr tmp_in_addr; + if (inet_pton(AF_INET6, working_state.server_address, &tmp_in_addr) == 1) { + char *new_server_address = calloc(strlen(working_state.server_address) + 3, sizeof(char)); + if (new_server_address == NULL) { + die(STATE_UNKNOWN, "HTTP UNKNOWN - Unable to allocate memory\n"); + } + snprintf(new_server_address, strlen(working_state.server_address) + 3, "[%s]", + working_state.server_address); + working_state.server_address = new_server_address; + } + + /* compose URL: use the address we want to connect to, set Host: header later */ + char url[DEFAULT_BUFFER_SIZE]; + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", + (working_state.use_ssl & (working_state.host_name != NULL)) + ? working_state.host_name + : working_state.server_address, + working_state.serverPort, working_state.server_url); + + if (verbose >= 1) { + printf("* curl CURLOPT_URL: %s\n", url); + } + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), + "CURLOPT_URL"); + + /* extract proxy information for legacy proxy https requests */ + if (!strcmp(working_state.http_method, "CONNECT") || + strstr(working_state.server_url, "http") == working_state.server_url) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXY, working_state.server_address), + "CURLOPT_PROXY"); + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYPORT, + (long)working_state.serverPort), + "CURLOPT_PROXYPORT"); + if (verbose >= 2) { + printf("* curl CURLOPT_PROXY: %s:%d\n", working_state.server_address, + working_state.serverPort); + } + working_state.http_method = "GET"; + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, working_state.server_url), + "CURLOPT_URL"); + } + + /* disable body for HEAD request */ + if (working_state.http_method && !strcmp(working_state.http_method, "HEAD")) { + working_state.no_body = true; + } + + /* set HTTP protocol version */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTP_VERSION, config.curl_http_version), + "CURLOPT_HTTP_VERSION"); + + /* set HTTP method */ + if (working_state.http_method) { + if (!strcmp(working_state.http_method, "POST")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_POST, 1), "CURLOPT_POST"); + } else if (!strcmp(working_state.http_method, "PUT")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD"); + } else { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_CUSTOMREQUEST, + working_state.http_method), + "CURLOPT_CUSTOMREQUEST"); + } + } + + char *force_host_header = NULL; + /* check if Host header is explicitly set in options */ + if (config.http_opt_headers_count) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { + if (strncmp(config.http_opt_headers[i], "Host:", 5) == 0) { + force_host_header = config.http_opt_headers[i]; + } + } + } + + /* set hostname (virtual hosts), not needed if CURLOPT_CONNECT_TO is used, but left in + * anyway */ + char http_header[DEFAULT_BUFFER_SIZE]; + if (working_state.host_name != NULL && force_host_header == NULL) { + if ((working_state.virtualPort != HTTP_PORT && !working_state.use_ssl) || + (working_state.virtualPort != HTTPS_PORT && working_state.use_ssl)) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", working_state.host_name, + working_state.virtualPort); + } else { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", working_state.host_name); + } + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); + } + + /* always close connection, be nice to servers */ + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Connection: close"); + result.curl_state.header_list = curl_slist_append(result.curl_state.header_list, http_header); + + /* attach additional headers supplied by the user */ + /* optionally send any other header tag */ + if (config.http_opt_headers_count) { + for (size_t i = 0; i < config.http_opt_headers_count; i++) { + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, config.http_opt_headers[i]); + } + } + + /* set HTTP headers */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_HTTPHEADER, result.curl_state.header_list), + "CURLOPT_HTTPHEADER"); + +#ifdef LIBCURL_FEATURE_SSL + /* set SSL version, warn about insecure or unsupported versions */ + if (working_state.use_ssl) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLVERSION, config.ssl_version), + "CURLOPT_SSLVERSION"); + } + + /* client certificate and key to present to server (SSL) */ + if (config.client_cert) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLCERT, config.client_cert), + "CURLOPT_SSLCERT"); + } + + if (config.client_privkey) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSLKEY, config.client_privkey), + "CURLOPT_SSLKEY"); + } + + if (config.ca_cert) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CAINFO, config.ca_cert), + "CURLOPT_CAINFO"); + } + + if (config.ca_cert || config.verify_peer_and_host) { + /* per default if we have a CA verify both the peer and the + * hostname in the certificate, can be switched off later */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 1), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 2), + "CURLOPT_SSL_VERIFYHOST"); + } else { + /* backward-compatible behaviour, be tolerant in checks + * TODO: depending on more options have aspects we want + * to be less tolerant about ssl verfications + */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYPEER, 0), + "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_VERIFYHOST, 0), + "CURLOPT_SSL_VERIFYHOST"); + } + + /* detect SSL library used by libcurl */ + curlhelp_ssl_library ssl_library = curlhelp_get_ssl_library(); + + /* try hard to get a stack of certificates to verify against */ + if (check_cert) { +# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) + /* inform curl to report back certificates */ + switch (ssl_library) { + case CURLHELP_SSL_LIBRARY_OPENSSL: + case CURLHELP_SSL_LIBRARY_LIBRESSL: + /* set callback to extract certificate with OpenSSL context function (works with + * OpenSSL-style libraries only!) */ +# ifdef USE_OPENSSL + /* libcurl and monitoring plugins built with OpenSSL, good */ + add_sslctx_verify_fun = true; + is_openssl_callback = true; +# endif /* USE_OPENSSL */ + /* libcurl is built with OpenSSL, monitoring plugins, so falling + * back to manually extracting certificate information */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + break; + + case CURLHELP_SSL_LIBRARY_NSS: +# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + /* NSS: support for CERTINFO is implemented since 7.34.0 */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); +# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", + curlhelp_get_ssl_library_string(ssl_library)); +# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + break; + + case CURLHELP_SSL_LIBRARY_GNUTLS: +# if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) + /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); +# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library " + "'%s' is too old)\n", + curlhelp_get_ssl_library_string(ssl_library)); +# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ + break; + + case CURLHELP_SSL_LIBRARY_UNKNOWN: + default: + die(STATE_CRITICAL, + "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must " + "implement first)\n", + curlhelp_get_ssl_library_string(ssl_library)); + break; + } +# else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ + /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ + if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || + ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) { + add_sslctx_verify_fun = true; + } else { + die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no " + "CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl " + "too old and has no CURLOPT_CERTINFO)\n"); + } +# endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ + } + +# if LIBCURL_VERSION_NUM >= \ + MAKE_LIBCURL_VERSION(7, 10, 6) /* required for CURLOPT_SSL_CTX_FUNCTION */ + // ssl ctx function is not available with all ssl backends + if (curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, NULL) != + CURLE_UNKNOWN_OPTION) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), + "CURLOPT_SSL_CTX_FUNCTION"); + } +# endif +#endif /* LIBCURL_FEATURE_SSL */ + + /* set default or user-given user agent identification */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERAGENT, config.user_agent), + "CURLOPT_USERAGENT"); + + /* proxy-authentication */ + if (strcmp(config.proxy_auth, "")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_PROXYUSERPWD, config.proxy_auth), + "CURLOPT_PROXYUSERPWD"); + } + + /* authentication */ + if (strcmp(config.user_auth, "")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_USERPWD, config.user_auth), + "CURLOPT_USERPWD"); + } + /* TODO: parameter auth method, bitfield of following methods: + * CURLAUTH_BASIC (default) + * CURLAUTH_DIGEST + * CURLAUTH_DIGEST_IE + * CURLAUTH_NEGOTIATE + * CURLAUTH_NTLM + * CURLAUTH_NTLM_WB + * + * convenience tokens for typical sets of methods: + * CURLAUTH_ANYSAFE: most secure, without BASIC + * or CURLAUTH_ANY: most secure, even BASIC if necessary + * + * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, + * (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH"); + */ + + /* handle redirections */ + if (on_redirect_dependent) { + if (follow_method == FOLLOW_LIBCURL) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_FOLLOWLOCATION, 1), + "CURLOPT_FOLLOWLOCATION"); + + /* default -1 is infinite, not good, could lead to zombie plugins! + Setting it to one bigger than maximal limit to handle errors nicely below + */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_MAXREDIRS, max_depth + 1), + "CURLOPT_MAXREDIRS"); + + /* for now allow only http and https (we are a http(s) check plugin in the end) */ +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 85, 0) + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"), + "CURLOPT_REDIR_PROTOCOLS_STR"); +#elif LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_REDIR_PROTOCOLS, + CURLPROTO_HTTP | CURLPROTO_HTTPS), + "CURLOPT_REDIRECT_PROTOCOLS"); +#endif + + /* TODO: handle the following aspects of redirection, make them + * command line options too later: + CURLOPT_POSTREDIR: method switch + CURLINFO_REDIRECT_URL: custom redirect option + CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols + CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range + option here is nice like for expected page size? + */ + } else { + /* old style redirection*/ + } + } + /* no-body */ + if (working_state.no_body) { + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_NOBODY, 1), + "CURLOPT_NOBODY"); + } + + /* IPv4 or IPv6 forced DNS resolution */ + if (config.sin_family == AF_UNSPEC) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); + } else if (config.sin_family == AF_INET) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); + } +#if defined(USE_IPV6) && defined(LIBCURL_FEATURE_IPV6) + else if (config.sin_family == AF_INET6) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), + "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); + } +#endif + + /* either send http POST data (any data, not only POST)*/ + if (!strcmp(working_state.http_method, "POST") || !strcmp(working_state.http_method, "PUT")) { + /* set content of payload for POST and PUT */ + if (config.http_content_type) { + snprintf(http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", + config.http_content_type); + result.curl_state.header_list = + curl_slist_append(result.curl_state.header_list, http_header); + } + /* NULL indicates "HTTP Continue" in libcurl, provide an empty string + * in case of no POST/PUT data */ + if (!working_state.http_post_data) { + working_state.http_post_data = ""; + } + + if (!strcmp(working_state.http_method, "POST")) { + /* POST method, set payload with CURLOPT_POSTFIELDS */ + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_POSTFIELDS, + working_state.http_post_data), + "CURLOPT_POSTFIELDS"); + } else if (!strcmp(working_state.http_method, "PUT")) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_READFUNCTION, + (curl_read_callback)curlhelp_buffer_read_callback), + "CURLOPT_READFUNCTION"); + if (curlhelp_initreadbuffer(&result.curl_state.put_buf, working_state.http_post_data, + strlen(working_state.http_post_data)) < 0) { + die(STATE_UNKNOWN, + "HTTP CRITICAL - out of memory allocating read buffer for PUT\n"); + } + result.curl_state.put_buf_initialized = true; + handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, + CURLOPT_READDATA, + (void *)&result.curl_state.put_buf), + "CURLOPT_READDATA"); + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, + (curl_off_t)strlen(working_state.http_post_data)), + "CURLOPT_INFILESIZE"); + } + } + + /* cookie handling */ + if (config.cookie_jar_file != NULL) { + /* enable reading cookies from a file, and if the filename is an empty string, only + * enable the curl cookie engine */ + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEFILE, config.cookie_jar_file), + "CURLOPT_COOKIEFILE"); + /* now enable saving cookies to a file, but only if the filename is not an empty string, + * since writing it would fail */ + if (*config.cookie_jar_file) { + handle_curl_option_return_code( + curl_easy_setopt(result.curl_state.curl, CURLOPT_COOKIEJAR, config.cookie_jar_file), + "CURLOPT_COOKIEJAR"); + } + } + + result.working_state = working_state; + + return result; +} + +void handle_curl_option_return_code(CURLcode res, const char *option) { + if (res != CURLE_OK) { + snprintf(msg, DEFAULT_BUFFER_SIZE, + _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, + curl_easy_strerror(res)); + die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + } +} + +char *get_header_value(const struct phr_header *headers, const size_t nof_headers, + const char *header) { + for (size_t i = 0; i < nof_headers; i++) { + if (headers[i].name != NULL && + strncasecmp(header, headers[i].name, max(headers[i].name_len, 4)) == 0) { + return strndup(headers[i].value, headers[i].value_len); + } + } + return NULL; +} + +check_curl_working_state check_curl_working_state_init() { + check_curl_working_state result = { + .server_address = NULL, + .server_url = DEFAULT_SERVER_URL, + .host_name = NULL, + .http_method = NULL, + .http_post_data = NULL, + .virtualPort = 0, + .serverPort = HTTP_PORT, + .use_ssl = false, + .no_body = false, + }; + return result; +} + +check_curl_config check_curl_config_init() { + check_curl_config tmp = { + .initial_config = check_curl_working_state_init(), + + .curl_config = + { + .automatic_decompression = false, + .socket_timeout = DEFAULT_SOCKET_TIMEOUT, + .haproxy_protocol = false, + .sin_family = AF_UNSPEC, + .curl_http_version = CURL_HTTP_VERSION_NONE, + .http_opt_headers = NULL, + .http_opt_headers_count = 0, + .ssl_version = CURL_SSLVERSION_DEFAULT, + .client_cert = NULL, + .client_privkey = NULL, + .ca_cert = NULL, + .verify_peer_and_host = false, + .user_agent = {'\0'}, + .proxy_auth = "", + .user_auth = "", + .http_content_type = NULL, + .cookie_jar_file = NULL, + }, + .max_depth = DEFAULT_MAX_REDIRS, + .followmethod = FOLLOW_HTTP_CURL, + .followsticky = STICKY_NONE, + + .maximum_age = -1, + .regexp = {}, + .compiled_regex = {}, + .state_regex = STATE_CRITICAL, + .invert_regex = false, + .check_cert = false, + .continue_after_check_cert = false, + .days_till_exp_warn = 0, + .days_till_exp_crit = 0, + .thlds = NULL, + .min_page_len = 0, + .max_page_len = 0, + .server_expect = + { + .string = HTTP_EXPECT, + .is_present = false, + }, + .string_expect = "", + .header_expect = "", + .on_redirect_result_state = STATE_OK, + .on_redirect_dependent = true, + + .show_extended_perfdata = false, + .show_body = false, + .display_html = false, + }; + + snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", + "check_curl", NP_VERSION, VERSION, curl_version()); + + return tmp; +} + +/* TODO: is there a better way in libcurl to check for the SSL library? */ +curlhelp_ssl_library curlhelp_get_ssl_library(void) { + curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; + + curl_version_info_data *version_data = curl_version_info(CURLVERSION_NOW); + if (version_data == NULL) { + return CURLHELP_SSL_LIBRARY_UNKNOWN; + } + + char *ssl_version = strdup(version_data->ssl_version); + if (ssl_version == NULL) { + return CURLHELP_SSL_LIBRARY_UNKNOWN; + } + + char *library = strtok(ssl_version, "/"); + if (library == NULL) { + return CURLHELP_SSL_LIBRARY_UNKNOWN; + } + + if (strcmp(library, "OpenSSL") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; + } else if (strcmp(library, "LibreSSL") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; + } else if (strcmp(library, "GnuTLS") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; + } else if (strcmp(library, "NSS") == 0) { + ssl_library = CURLHELP_SSL_LIBRARY_NSS; + } + + if (verbose >= 2) { + printf("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, + ssl_library); + } + + free(ssl_version); + + return ssl_library; +} + +const char *curlhelp_get_ssl_library_string(const curlhelp_ssl_library ssl_library) { + switch (ssl_library) { + case CURLHELP_SSL_LIBRARY_OPENSSL: + return "OpenSSL"; + case CURLHELP_SSL_LIBRARY_LIBRESSL: + return "LibreSSL"; + case CURLHELP_SSL_LIBRARY_GNUTLS: + return "GnuTLS"; + case CURLHELP_SSL_LIBRARY_NSS: + return "NSS"; + case CURLHELP_SSL_LIBRARY_UNKNOWN: + default: + return "unknown"; + } +} + +size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf) { + struct phr_header headers[255]; + size_t nof_headers = 255; + size_t msglen; + curlhelp_statusline status_line; + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); + + if (res == -1) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); + } + + char *content_length_s = get_header_value(headers, nof_headers, "content-length"); + if (!content_length_s) { + return header_buf->buflen + body_buf->buflen; + } + + content_length_s += strspn(content_length_s, " \t"); + size_t content_length = atoi(content_length_s); + if (content_length != body_buf->buflen) { + /* TODO: should we warn if the actual and the reported body length don't match? */ + } + + if (content_length_s) { + free(content_length_s); + } + + return header_buf->buflen + body_buf->buflen; +} + +mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, + const char msg[static DEFAULT_BUFFER_SIZE], + const int maximum_age) { + struct phr_header headers[255]; + size_t nof_headers = 255; + curlhelp_statusline status_line; + size_t msglen; + int res = phr_parse_response(header_buf->buf, header_buf->buflen, &status_line.http_major, + &status_line.http_minor, &status_line.http_code, &status_line.msg, + &msglen, headers, &nof_headers, 0); + + if (res == -1) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Failed to parse Response\n")); + } + + char *server_date = get_header_value(headers, nof_headers, "date"); + char *document_date = get_header_value(headers, nof_headers, "last-modified"); + + mp_state_enum date_result = STATE_OK; + if (!server_date || !*server_date) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), msg); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_UNKNOWN, date_result); + + } else if (!document_date || !*document_date) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), msg); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + + } else { + time_t srv_data = curl_getdate(server_date, NULL); + time_t doc_data = curl_getdate(document_date, NULL); + if (verbose >= 2) { + printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, + document_date, (int)doc_data); + } + if (srv_data <= 0) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), msg, + server_date); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data <= 0) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), msg, + document_date); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data > srv_data + 30) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), msg, + (int)doc_data - (int)srv_data); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else if (doc_data < srv_data - maximum_age) { + time_t last_modified = (srv_data - doc_data); + if (last_modified > (60 * 60 * 24 * 2)) { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), msg, + ((float)last_modified) / (60 * 60 * 24)); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } else { + char tmp[DEFAULT_BUFFER_SIZE]; + + snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), msg, + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + strcpy(msg, tmp); + + date_result = max_state_alt(STATE_CRITICAL, date_result); + } + } + } + + if (server_date) { + free(server_date); + } + if (document_date) { + free(document_date); + } + + return date_result; +} + +void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } + +int curlhelp_parse_statusline(const char *buf, curlhelp_statusline *status_line) { + /* find last start of a new header */ + const char *start = strrstr2(buf, "\r\nHTTP/"); + if (start != NULL) { + start += 2; + buf = start; + } + + char *first_line_end = strstr(buf, "\r\n"); + if (first_line_end == NULL) { + return -1; + } + + size_t first_line_len = (size_t)(first_line_end - buf); + status_line->first_line = (char *)calloc(first_line_len + 1, sizeof(char)); + if (status_line->first_line == NULL) { + return -1; + } + memcpy(status_line->first_line, buf, first_line_len); + status_line->first_line[first_line_len] = '\0'; + char *first_line_buf = strdup(status_line->first_line); + + /* protocol and version: "HTTP/x.x" SP or "HTTP/2" SP */ + char *temp_string = strtok(first_line_buf, "/"); + if (temp_string == NULL) { + free(first_line_buf); + return -1; + } + if (strcmp(temp_string, "HTTP") != 0) { + free(first_line_buf); + return -1; + } + + temp_string = strtok(NULL, " "); + if (temp_string == NULL) { + free(first_line_buf); + return -1; + } + + char *temp_string_2; + if (strchr(temp_string, '.') != NULL) { + + /* HTTP 1.x case */ + strtok(temp_string, "."); + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + strtok(NULL, " "); + status_line->http_minor = (int)strtol(temp_string, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + temp_string += 4; /* 1.x SP */ + } else { + /* HTTP 2 case */ + status_line->http_major = (int)strtol(temp_string, &temp_string_2, 10); + status_line->http_minor = 0; + temp_string += 2; /* 2 SP */ + } + + /* status code: "404" or "404.1", then SP */ + temp_string = strtok(temp_string, " "); + if (temp_string == NULL) { + free(first_line_buf); + return -1; + } + if (strchr(temp_string, '.') != NULL) { + char *ppp; + ppp = strtok(temp_string, "."); + status_line->http_code = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + ppp = strtok(NULL, ""); + status_line->http_subcode = (int)strtol(ppp, &temp_string_2, 10); + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + temp_string += 6; /* 400.1 SP */ + } else { + status_line->http_code = (int)strtol(temp_string, &temp_string_2, 10); + status_line->http_subcode = -1; + if (*temp_string_2 != '\0') { + free(first_line_buf); + return -1; + } + temp_string += 4; /* 400 SP */ + } + + /* Human readable message: "Not Found" CRLF */ + + temp_string = strtok(temp_string, ""); + if (temp_string == NULL) { + status_line->msg = ""; + return 0; + } + status_line->msg = status_line->first_line + (temp_string - first_line_buf); + free(first_line_buf); + + return 0; +} + +/* TODO: where to put this, it's actually part of sstrings2 (logically)? + */ +const char *strrstr2(const char *haystack, const char *needle) { + if (haystack == NULL || needle == NULL) { + return NULL; + } + + if (haystack[0] == '\0' || needle[0] == '\0') { + return NULL; + } + + int counter = 0; + const char *prev_pos = NULL; + const char *pos = haystack; + size_t len = strlen(needle); + for (;;) { + pos = strstr(pos, needle); + if (pos == NULL) { + if (counter == 0) { + return NULL; + } + return prev_pos; + } + counter++; + prev_pos = pos; + pos += len; + if (*pos == '\0') { + return prev_pos; + } + } +} + +void curlhelp_freereadbuffer(curlhelp_read_curlbuf *buf) { + free(buf->buf); + buf->buf = NULL; +} + +void curlhelp_freewritebuffer(curlhelp_write_curlbuf *buf) { + free(buf->buf); + buf->buf = NULL; +} + +int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char *data, size_t datalen) { + if ((*buf = calloc(1, sizeof(curlhelp_read_curlbuf))) == NULL) { + return 1; + } + + (*buf)->buflen = datalen; + (*buf)->buf = (char *)calloc((*buf)->buflen, sizeof(char)); + if ((*buf)->buf == NULL) { + return -1; + } + memcpy((*buf)->buf, data, datalen); + (*buf)->pos = 0; + return 0; +} + +size_t curlhelp_buffer_read_callback(void *buffer, size_t size, size_t nmemb, void *stream) { + curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream; + + size_t minimalSize = min(nmemb * size, buf->buflen - buf->pos); + + memcpy(buffer, buf->buf + buf->pos, minimalSize); + buf->pos += minimalSize; + + return minimalSize; +} + +int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf) { + if ((*buf = calloc(1, sizeof(curlhelp_write_curlbuf))) == NULL) { + return 1; + } + (*buf)->bufsize = DEFAULT_BUFFER_SIZE * sizeof(char); + (*buf)->buflen = 0; + (*buf)->buf = (char *)calloc((*buf)->bufsize, sizeof(char)); + if ((*buf)->buf == NULL) { + return -1; + } + return 0; +} + +size_t curlhelp_buffer_write_callback(void *buffer, size_t size, size_t nmemb, void *stream) { + curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream; + + while (buf->bufsize < buf->buflen + size * nmemb + 1) { + buf->bufsize = buf->bufsize * 2; + buf->buf = (char *)realloc(buf->buf, buf->bufsize); + if (buf->buf == NULL) { + fprintf(stderr, "malloc failed (%d) %s\n", errno, strerror(errno)); + return 0; + } + } + + memcpy(buf->buf + buf->buflen, buffer, size * nmemb); + buf->buflen += size * nmemb; + buf->buf[buf->buflen] = '\0'; + + return size * nmemb; +} + +void cleanup(check_curl_global_state global_state) { + if (global_state.status_line_initialized) { + curlhelp_free_statusline(global_state.status_line); + } + global_state.status_line_initialized = false; + + if (global_state.curl_easy_initialized) { + curl_easy_cleanup(global_state.curl); + } + global_state.curl_easy_initialized = false; + + if (global_state.curl_global_initialized) { + curl_global_cleanup(); + } + global_state.curl_global_initialized = false; + + if (global_state.body_buf_initialized) { + curlhelp_freewritebuffer(global_state.body_buf); + } + global_state.body_buf_initialized = false; + + if (global_state.header_buf_initialized) { + curlhelp_freewritebuffer(global_state.header_buf); + } + global_state.header_buf_initialized = false; + + if (global_state.put_buf_initialized) { + curlhelp_freereadbuffer(global_state.put_buf); + } + global_state.put_buf_initialized = false; + + if (global_state.header_list) { + curl_slist_free_all(global_state.header_list); + } + + if (global_state.host) { + curl_slist_free_all(global_state.host); + } +} + +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family) { + struct addrinfo hints = { + .ai_family = addr_family, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + }; + + struct addrinfo *result; + int errcode = getaddrinfo(host, NULL, &hints, &result); + if (errcode != 0) { + return errcode; + } + + strcpy(buf, ""); + struct addrinfo *res = result; + + size_t buflen_remaining = buflen - 1; + size_t addrstr_len; + char addrstr[100]; + void *ptr = {0}; + while (res) { + switch (res->ai_family) { + case AF_INET: + ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + } + + inet_ntop(res->ai_family, ptr, addrstr, 100); + if (verbose >= 1) { + printf("* getaddrinfo IPv%d address: %s\n", res->ai_family == PF_INET6 ? 6 : 4, + addrstr); + } + + // Append all IPs to buf as a comma-separated string + addrstr_len = strlen(addrstr); + if (buflen_remaining > addrstr_len + 1) { + if (buf[0] != '\0') { + strncat(buf, ",", buflen_remaining); + buflen_remaining -= 1; + } + strncat(buf, addrstr, buflen_remaining); + buflen_remaining -= addrstr_len; + } + + res = res->ai_next; + } + + freeaddrinfo(result); + + return 0; +} + +/* Checks if the server 'reply' is one of the expected 'statuscodes' */ +bool expected_statuscode(const char *reply, const char *statuscodes) { + char *expected; + + if ((expected = strdup(statuscodes)) == NULL) { + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n")); + } + + bool result = false; + for (char *code = strtok(expected, ","); code != NULL; code = strtok(NULL, ",")) { + if (strstr(reply, code) != NULL) { + result = true; + break; + } + } + + free(expected); + return result; +} + +/* returns a string "HTTP/1.x" or "HTTP/2" */ +char *string_statuscode(int major, int minor) { + static char buf[10]; + + switch (major) { + case 1: + snprintf(buf, sizeof(buf), "HTTP/%d.%d", major, minor); + break; + case 2: + case 3: + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + default: + /* assuming here HTTP/N with N>=4 */ + snprintf(buf, sizeof(buf), "HTTP/%d", major); + break; + } + + return buf; +} + +char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { + return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), + thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), + thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); +} + +char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { + return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, + socket_timeout); +} + +char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { + return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, + socket_timeout); +} + +char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { + return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, + socket_timeout); +} + +char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { + return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, + true, socket_timeout); +} + +char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { + return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, + true, socket_timeout); +} + +char *perfd_size(size_t page_len, int min_page_len) { + return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, + true, 0, false, 0); +} + +/* check whether a file exists */ +void test_file(char *path) { + if (access(path, R_OK) == 0) { + return; + } + usage2(_("file does not exist or is not readable"), path); +} diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h new file mode 100644 index 00000000..0f43ab90 --- /dev/null +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -0,0 +1,125 @@ +#include "./config.h" +#include +#include "../picohttpparser/picohttpparser.h" +// #include "curl/easy.h" + +/* for buffers for header and body */ +typedef struct { + size_t buflen; + size_t bufsize; + char *buf; +} curlhelp_write_curlbuf; + +/* for buffering the data sent in PUT */ +typedef struct { + size_t buflen; + off_t pos; + char *buf; +} curlhelp_read_curlbuf; + +/* for parsing the HTTP status line */ +typedef struct { + int http_major; /* major version of the protocol, always 1 (HTTP/0.9 + * never reached the big internet most likely) */ + int http_minor; /* minor version of the protocol, usually 0 or 1 */ + int http_code; /* HTTP return code as in RFC 2145 */ + int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see + * http://support.microsoft.com/kb/318380/en-us */ + const char *msg; /* the human readable message */ + char *first_line; /* a copy of the first line */ +} curlhelp_statusline; + +typedef struct { + bool curl_global_initialized; + bool curl_easy_initialized; + + bool body_buf_initialized; + curlhelp_write_curlbuf *body_buf; + + bool header_buf_initialized; + curlhelp_write_curlbuf *header_buf; + + bool status_line_initialized; + curlhelp_statusline *status_line; + + bool put_buf_initialized; + curlhelp_read_curlbuf *put_buf; + + CURL *curl; + + struct curl_slist *header_list; + struct curl_slist *host; +} check_curl_global_state; + +/* to know the underlying SSL library used by libcurl */ +typedef enum curlhelp_ssl_library { + CURLHELP_SSL_LIBRARY_UNKNOWN, + CURLHELP_SSL_LIBRARY_OPENSSL, + CURLHELP_SSL_LIBRARY_LIBRESSL, + CURLHELP_SSL_LIBRARY_GNUTLS, + CURLHELP_SSL_LIBRARY_NSS +} curlhelp_ssl_library; + +#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major) * 0x10000 + (minor) * 0x100 + (patch)) + +typedef struct { + int errorcode; + check_curl_global_state curl_state; + check_curl_working_state working_state; +} check_curl_configure_curl_wrapper; + +check_curl_configure_curl_wrapper check_curl_configure_curl(check_curl_static_curl_config config, + check_curl_working_state working_state, + bool check_cert, + bool on_redirect_dependent, + int follow_method, int max_depth); + +void handle_curl_option_return_code(CURLcode res, const char *option); + +int curlhelp_initwritebuffer(curlhelp_write_curlbuf **buf); +size_t curlhelp_buffer_write_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); +void curlhelp_freewritebuffer(curlhelp_write_curlbuf * /*buf*/); + +int curlhelp_initreadbuffer(curlhelp_read_curlbuf **buf, const char * /*data*/, size_t /*datalen*/); +size_t curlhelp_buffer_read_callback(void * /*buffer*/, size_t /*size*/, size_t /*nmemb*/, + void * /*stream*/); +void curlhelp_freereadbuffer(curlhelp_read_curlbuf * /*buf*/); + +curlhelp_ssl_library curlhelp_get_ssl_library(void); +const char *curlhelp_get_ssl_library_string(curlhelp_ssl_library /*ssl_library*/); + +typedef union { + struct curl_slist *to_info; + struct curl_certinfo *to_certinfo; +} cert_ptr_union; +int net_noopenssl_check_certificate(cert_ptr_union *, int, int); + +int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*status_line*/); +void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); + +char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); +mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + const char msg[static DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +size_t get_content_length(const curlhelp_write_curlbuf *header_buf, + const curlhelp_write_curlbuf *body_buf); +int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); +CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm); + +#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN +const char *strrstr2(const char *haystack, const char *needle); + +void cleanup(check_curl_global_state global_state); + +bool expected_statuscode(const char *reply, const char *statuscodes); +char *string_statuscode(int major, int minor); + +char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); +char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); +char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); +char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); +char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); +char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); +char *perfd_size(size_t page_len, int /*min_page_len*/); + +void test_file(char *path); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index be25d1bb..a4b1eecf 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -49,20 +49,7 @@ typedef struct { bool no_body; } check_curl_working_state; -check_curl_working_state check_curl_working_state_init() { - check_curl_working_state result = { - .server_address = NULL, - .server_url = DEFAULT_SERVER_URL, - .host_name = NULL, - .http_method = NULL, - .http_post_data = NULL, - .virtualPort = 0, - .serverPort = HTTP_PORT, - .use_ssl = false, - .no_body = false, - }; - return result; -} +check_curl_working_state check_curl_working_state_init(); typedef struct { bool automatic_decompression; @@ -123,63 +110,4 @@ typedef struct { bool display_html; } check_curl_config; -check_curl_config check_curl_config_init() { - check_curl_config tmp = { - .initial_config = check_curl_working_state_init(), - - .curl_config = - { - .automatic_decompression = false, - .socket_timeout = DEFAULT_SOCKET_TIMEOUT, - .haproxy_protocol = false, - .sin_family = AF_UNSPEC, - .curl_http_version = CURL_HTTP_VERSION_NONE, - .http_opt_headers = NULL, - .http_opt_headers_count = 0, - .ssl_version = CURL_SSLVERSION_DEFAULT, - .client_cert = NULL, - .client_privkey = NULL, - .ca_cert = NULL, - .verify_peer_and_host = false, - .user_agent = {'\0'}, - .proxy_auth = "", - .user_auth = "", - .http_content_type = NULL, - .cookie_jar_file = NULL, - }, - .max_depth = DEFAULT_MAX_REDIRS, - .followmethod = FOLLOW_HTTP_CURL, - .followsticky = STICKY_NONE, - - .maximum_age = -1, - .regexp = {}, - .compiled_regex = {}, - .state_regex = STATE_CRITICAL, - .invert_regex = false, - .check_cert = false, - .continue_after_check_cert = false, - .days_till_exp_warn = 0, - .days_till_exp_crit = 0, - .thlds = NULL, - .min_page_len = 0, - .max_page_len = 0, - .server_expect = - { - .string = HTTP_EXPECT, - .is_present = false, - }, - .string_expect = "", - .header_expect = "", - .on_redirect_result_state = STATE_OK, - .on_redirect_dependent = true, - - .show_extended_perfdata = false, - .show_body = false, - .display_html = false, - }; - - snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", - "check_curl", NP_VERSION, VERSION, curl_version()); - - return tmp; -} +check_curl_config check_curl_config_init(); -- cgit v1.2.3-74-g34f1 From ad4faf1a9a414c657a94cafb54e5b3f1a5f5aaa2 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:35:45 +0200 Subject: Fix struct access --- plugins/check_curl.c | 2 +- plugins/check_curl.d/check_curl_helpers.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b747ec99..b0dde1eb 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -1275,7 +1275,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_3; #else - result.config.curl_http_version = CURL_HTTP_VERSION_NONE; + result.config.curl_config.curl_http_version = CURL_HTTP_VERSION_NONE; #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 66, 0) */ } else { fprintf(stderr, "unknown http-version parameter: %s\n", optarg); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index 42d63bc4..29aae524 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -516,7 +516,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, result.curl_state.put_buf_initialized = true; handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_READDATA, - (void *)&result.curl_state.put_buf), + (void *)result.curl_state.put_buf), "CURLOPT_READDATA"); handle_curl_option_return_code( curl_easy_setopt(result.curl_state.curl, CURLOPT_INFILESIZE, -- cgit v1.2.3-74-g34f1 From 9c2ae47d4494c524f3889722fba0cb5619c1e053 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 12:36:00 +0200 Subject: check_curl: fix default redirect setting --- plugins/check_curl.d/check_curl_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index 29aae524..e33f2aa9 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -629,7 +629,7 @@ check_curl_config check_curl_config_init() { .string_expect = "", .header_expect = "", .on_redirect_result_state = STATE_OK, - .on_redirect_dependent = true, + .on_redirect_dependent = false, .show_extended_perfdata = false, .show_body = false, -- cgit v1.2.3-74-g34f1 From aaff3aa9da27ff7666d2a776d524c784b76eb3d7 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:36:48 +0200 Subject: lib: some formatting + remove some unnecessary stuff --- lib/perfdata.h | 46 +++++++++++++++++++++++----------------------- lib/thresholds.h | 4 ++-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/perfdata.h b/lib/perfdata.h index c5d4a61d..e51ef5fd 100644 --- a/lib/perfdata.h +++ b/lib/perfdata.h @@ -28,7 +28,7 @@ typedef struct { /* * New range type with generic numerical values */ -typedef struct mp_range_struct { +typedef struct { mp_perfdata_value start; bool start_infinity; /* false (default) or true */ @@ -41,7 +41,7 @@ typedef struct mp_range_struct { /* * Old range type with floating point values */ -typedef struct range_struct { +typedef struct { double start; bool start_infinity; double end; @@ -53,7 +53,7 @@ typedef struct range_struct { /* * Perfdata type for storing perfdata output */ -typedef struct perfdata_struct { +typedef struct { char *label; char *uom; mp_perfdata_value value; @@ -131,15 +131,15 @@ mp_range_parsed mp_parse_range_string(const char * /*input*/); */ void pd_list_append(pd_list[1], mp_perfdata); -#define mp_set_pd_value(P, V) \ - _Generic((V), \ - float: mp_set_pd_value_float, \ - double: mp_set_pd_value_double, \ - int: mp_set_pd_value_int, \ - unsigned int: mp_set_pd_value_u_int, \ - long: mp_set_pd_value_long, \ - unsigned long: mp_set_pd_value_u_long, \ - long long: mp_set_pd_value_long_long, \ +#define mp_set_pd_value(P, V) \ + _Generic((V), \ + float: mp_set_pd_value_float, \ + double: mp_set_pd_value_double, \ + int: mp_set_pd_value_int, \ + unsigned int: mp_set_pd_value_u_int, \ + long: mp_set_pd_value_long, \ + unsigned long: mp_set_pd_value_u_long, \ + long long: mp_set_pd_value_long_long, \ unsigned long long: mp_set_pd_value_u_long_long)(P, V) mp_perfdata mp_set_pd_value_float(mp_perfdata, float); @@ -151,17 +151,17 @@ mp_perfdata mp_set_pd_value_u_long(mp_perfdata, unsigned long); mp_perfdata mp_set_pd_value_long_long(mp_perfdata, long long); mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long); -#define mp_create_pd_value(V) \ - _Generic((V), \ - float: mp_create_pd_value_float, \ - double: mp_create_pd_value_double, \ - char: mp_create_pd_value_char, \ - unsigned char: mp_create_pd_value_u_char, \ - int: mp_create_pd_value_int, \ - unsigned int: mp_create_pd_value_u_int, \ - long: mp_create_pd_value_long, \ - unsigned long: mp_create_pd_value_u_long, \ - long long: mp_create_pd_value_long_long, \ +#define mp_create_pd_value(V) \ + _Generic((V), \ + float: mp_create_pd_value_float, \ + double: mp_create_pd_value_double, \ + char: mp_create_pd_value_char, \ + unsigned char: mp_create_pd_value_u_char, \ + int: mp_create_pd_value_int, \ + unsigned int: mp_create_pd_value_u_int, \ + long: mp_create_pd_value_long, \ + unsigned long: mp_create_pd_value_u_long, \ + long long: mp_create_pd_value_long_long, \ unsigned long long: mp_create_pd_value_u_long_long)(V) mp_perfdata_value mp_create_pd_value_float(float); diff --git a/lib/thresholds.h b/lib/thresholds.h index 5f9f9247..f8647681 100644 --- a/lib/thresholds.h +++ b/lib/thresholds.h @@ -6,12 +6,12 @@ /* * Old threshold type using the old range type */ -typedef struct thresholds_struct { +typedef struct { range *warning; range *critical; } thresholds; -typedef struct mp_thresholds_struct { +typedef struct { bool warning_is_set; mp_range warning; bool critical_is_set; -- cgit v1.2.3-74-g34f1 From 44b2a25a6b9fb0791ec7150100a4d51e5f129611 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:37:24 +0200 Subject: check_curl: implement new output mechanism --- plugins/check_curl.c | 562 +++++++++++++++--------------- plugins/check_curl.d/check_curl_helpers.c | 251 +++++++------ plugins/check_curl.d/check_curl_helpers.h | 21 +- plugins/check_curl.d/config.h | 7 +- 4 files changed, 443 insertions(+), 398 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b0dde1eb..722666dd 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -42,7 +42,10 @@ const char *email = "devel@monitoring-plugins.org"; #include "thresholds.h" #include #include +#include "output.h" +#include "perfdata.h" +#include #include "common.h" #include "utils.h" #include "./check_curl.d/check_curl_helpers.h" @@ -78,7 +81,6 @@ enum { // Globals int verbose = 0; -extern char msg[DEFAULT_BUFFER_SIZE]; extern char errbuf[MAX_INPUT_BUFFER]; extern bool is_openssl_callback; extern bool add_sslctx_verify_fun; @@ -93,8 +95,8 @@ typedef struct { } check_curl_config_wrapper; static check_curl_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static mp_state_enum check_http(check_curl_config /*config*/, check_curl_working_state workingState, - int redir_depth); +static mp_subcheck check_http(check_curl_config /*config*/, check_curl_working_state workingState, + int redir_depth); typedef struct { int redir_depth; @@ -149,7 +151,12 @@ int main(int argc, char **argv) { check_curl_working_state working_state = config.initial_config; - exit((int)check_http(config, working_state, 0)); + mp_check overall = mp_check_init(); + mp_subcheck sc_test = check_http(config, working_state, 0); + + mp_add_subcheck_to_check(&overall, sc_test); + + mp_exit(overall); } #ifdef HAVE_SSL @@ -200,8 +207,8 @@ CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm) { # endif /* USE_OPENSSL */ #endif /* HAVE_SSL */ -mp_state_enum check_http(const check_curl_config config, check_curl_working_state workingState, - int redir_depth) { +mp_subcheck check_http(const check_curl_config config, check_curl_working_state workingState, + int redir_depth) { // ======================= // Initialisation for curl @@ -213,6 +220,13 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat check_curl_global_state curl_state = conf_curl_struct.curl_state; workingState = conf_curl_struct.working_state; + mp_subcheck sc_result = mp_subcheck_init(); + + char *url = fmt_url(workingState); + xasprintf(&sc_result.output, "Testing %s", url); + // TODO add some output here URL or something + free(url); + // ============== // do the request // ============== @@ -222,172 +236,149 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat printf("**** REQUEST CONTENT ****\n%s\n", workingState.http_post_data); } + mp_subcheck sc_curl = mp_subcheck_init(); + /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), - workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + xasprintf(&sc_curl.output, + _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), + workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL); + mp_add_subcheck_to_subcheck(&sc_result, sc_curl); + return sc_result; } + xasprintf(&sc_curl.output, "cURL performed query"); + sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK); + mp_add_subcheck_to_subcheck(&sc_result, sc_curl); + // ========== // Evaluation // ========== - mp_state_enum result_ssl = STATE_OK; -/* certificate checks */ #ifdef LIBCURL_FEATURE_SSL - if (workingState.use_ssl) { - if (config.check_cert) { - if (is_openssl_callback) { -# ifdef USE_OPENSSL - /* check certificate with OpenSSL functions, curl has been built against OpenSSL - * and we actually have OpenSSL in the monitoring tools - */ - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, - config.days_till_exp_crit); - if (!config.continue_after_check_cert) { - return result_ssl; - } -# else /* USE_OPENSSL */ - die(STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " - "callback used and not linked against OpenSSL\n"); -# endif /* USE_OPENSSL */ - } else { - struct curl_slist *slist; + if (workingState.use_ssl && config.check_cert) { + mp_subcheck sc_certificate = check_curl_certificate_checks( + curl_state.curl, cert, config.days_till_exp_warn, config.days_till_exp_crit); - cert_ptr_union cert_ptr = {0}; - cert_ptr.to_info = NULL; - res = curl_easy_getinfo(curl_state.curl, CURLINFO_CERTINFO, &cert_ptr.to_info); - if (!res && cert_ptr.to_info) { -# ifdef USE_OPENSSL - /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert - * parsing We only check the first certificate and assume it's the one of - * the server - */ - char *raw_cert = NULL; - bool got_first_cert = false; - for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { - if (got_first_cert) { - break; - } - - for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; - slist = slist->next) { - if (verbose >= 2) { - printf("%d ** %s\n", i, slist->data); - } - if (strncmp(slist->data, "Cert:", 5) == 0) { - raw_cert = &slist->data[5]; - got_first_cert = true; - break; - } - } - } - - if (!raw_cert) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Cannot retrieve certificates from CERTINFO information - " - "certificate data was empty")); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - BIO *cert_BIO = BIO_new(BIO_s_mem()); - BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert)); - cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); - if (!cert) { - snprintf( - msg, DEFAULT_BUFFER_SIZE, - _("Cannot read certificate from CERTINFO information - BIO error")); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - BIO_free(cert_BIO); - result_ssl = np_net_ssl_check_certificate(cert, config.days_till_exp_warn, - config.days_till_exp_crit); - if (!config.continue_after_check_cert) { - return result_ssl; - } -# else /* USE_OPENSSL */ - /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our - * disposal, so we use the libcurl CURLINFO data - */ - result_ssl = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, - days_till_exp_crit); - if (!continue_after_check_cert) { - return result_ssl; - } -# endif /* USE_OPENSSL */ - } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Cannot retrieve certificates - cURL returned %d - %s"), res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); - } - } + mp_add_subcheck_to_subcheck(&sc_result, sc_certificate); + if (!config.continue_after_check_cert) { + // TODO finish here then } } -#endif /* LIBCURL_FEATURE_SSL */ +#endif /* we got the data and we executed the request in a given time, so we can append * performance data to the answer always */ + + // total time the query took + mp_perfdata pd_total_time = perfdata_init(); double total_time; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME"); - size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); - char perfstring[DEFAULT_BUFFER_SIZE]; + mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time); + pd_total_time.value = pd_val_total_time; + pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds); + pd_total_time.label = "time"; + pd_total_time.uom = "s"; + + mp_subcheck sc_total_time = mp_subcheck_init(); + sc_total_time = mp_set_subcheck_state(sc_total_time, mp_get_pd_status(pd_total_time)); + xasprintf(&sc_total_time.output, "Total connection time: %fs", total_time); + mp_add_perfdata_to_subcheck(&sc_total_time, pd_total_time); + + mp_add_subcheck_to_subcheck(&sc_result, sc_total_time); + if (config.show_extended_perfdata) { + // overall connection time + mp_perfdata pd_time_connect = perfdata_init(); double time_connect; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME"); + mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect); + pd_time_connect.value = pd_val_time_connect; + pd_time_connect.label = "time"; + pd_time_connect.uom = "s"; + pd_time_connect = mp_set_pd_max_value( + pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout)); + + pd_time_connect = mp_pd_set_thresholds(pd_time_connect, config.thlds); + mp_add_perfdata_to_subcheck(&sc_result, pd_time_connect); + + // application connection time, used to compute other timings double time_appconnect; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME"); - double time_headers; - handle_curl_option_return_code( - curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), - "CURLINFO_PRETRANSFER_TIME"); + if (workingState.use_ssl) { + mp_perfdata pd_time_tls = perfdata_init(); + { + mp_perfdata_value pd_val_time_tls = + mp_create_pd_value(time_appconnect - time_connect); + + pd_time_tls.value = pd_val_time_tls; + } + pd_time_tls.label = "time_tls"; + pd_time_tls.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_tls); + } + + mp_perfdata pd_time_headers = perfdata_init(); + { + double time_headers; + handle_curl_option_return_code( + curl_easy_getinfo(curl_state.curl, CURLINFO_PRETRANSFER_TIME, &time_headers), + "CURLINFO_PRETRANSFER_TIME"); + + mp_perfdata_value pd_val_time_headers = + mp_create_pd_value(time_headers - time_appconnect); + + pd_time_headers.value = pd_val_time_headers; + } + pd_time_headers.label = "time_headers"; + pd_time_headers.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_headers); + mp_perfdata pd_time_firstbyte = perfdata_init(); double time_firstbyte; handle_curl_option_return_code( curl_easy_getinfo(curl_state.curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME"); - snprintf( - perfstring, DEFAULT_BUFFER_SIZE, "%s %s %s %s %s %s %s", - perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), - perfd_size(page_len, config.min_page_len), - perfd_time_connect(time_connect, config.curl_config.socket_timeout), - workingState.use_ssl - ? perfd_time_ssl(time_appconnect - time_connect, config.curl_config.socket_timeout) - : "", - perfd_time_headers(time_headers - time_appconnect, config.curl_config.socket_timeout), - perfd_time_firstbyte(time_firstbyte - time_headers, config.curl_config.socket_timeout), - perfd_time_transfer(total_time - time_firstbyte, config.curl_config.socket_timeout)); - } else { - snprintf(perfstring, DEFAULT_BUFFER_SIZE, "%s %s", - perfd_time(total_time, config.thlds, config.curl_config.socket_timeout), - perfd_size(page_len, config.min_page_len)); + mp_perfdata_value pd_val_time_firstbyte = mp_create_pd_value(time_firstbyte); + pd_time_firstbyte.value = pd_val_time_firstbyte; + pd_time_firstbyte.label = "time_firstbyte"; + pd_time_firstbyte.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_firstbyte); + + mp_perfdata pd_time_transfer = perfdata_init(); + pd_time_transfer.value = mp_create_pd_value(total_time - time_firstbyte); + pd_time_transfer.label = "time_transfer"; + pd_time_transfer.uom = "s"; + mp_add_perfdata_to_subcheck(&sc_result, pd_time_transfer); } /* return a CRITICAL status if we couldn't read any data */ if (strlen(curl_state.header_buf->buf) == 0 && strlen(curl_state.body_buf->buf) == 0) { - die(STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); + sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); + xasprintf(&sc_result.output, "No header received from host"); + return sc_result; } /* get status line of answer, check sanity of HTTP code */ if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - "Unparsable status line in %.3g seconds response time|%s\n", total_time, - perfstring); + sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); /* we cannot know the major/minor version here for sure as we cannot parse the first * line */ - die(STATE_CRITICAL, "HTTP CRITICAL HTTP/x.x unknown - %s", msg); + xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line"); + return sc_result; } + curl_state.status_line_initialized = true; /* get result code from cURL */ @@ -406,43 +397,71 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat } /* make sure the status line matches the response we are looking for */ + mp_subcheck sc_expect = mp_subcheck_init(); + sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_OK); if (!expected_statuscode(curl_state.status_line->first_line, config.server_expect.string)) { if (workingState.serverPort == HTTP_PORT) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), - curl_state.status_line->first_line); + xasprintf(&sc_expect.output, _("Invalid HTTP response received from host: %s\n"), + curl_state.status_line->first_line); } else { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Invalid HTTP response received from host on port %d: %s\n"), - workingState.serverPort, curl_state.status_line->first_line); + xasprintf(&sc_expect.output, + _("Invalid HTTP response received from host on port %d: %s\n"), + workingState.serverPort, curl_state.status_line->first_line); } - die(STATE_CRITICAL, "HTTP CRITICAL - %s%s%s", msg, config.show_body ? "\n" : "", - config.show_body ? curl_state.body_buf->buf : ""); + sc_expect = mp_set_subcheck_default_state(sc_expect, STATE_CRITICAL); + } else { + xasprintf(&sc_expect.output, _("Status line output matched \"%s\""), + config.server_expect.string); } + mp_add_subcheck_to_subcheck(&sc_result, sc_expect); - mp_state_enum result = STATE_OK; - if (config.server_expect.is_present) { - snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), - config.server_expect.string); - if (verbose) { - printf("%s\n", msg); - } - result = STATE_OK; - } else { + if (!config.server_expect.is_present) { /* illegal return codes result in a critical state */ + mp_subcheck sc_return_code = mp_subcheck_init(); + sc_return_code = mp_set_subcheck_default_state(sc_return_code, STATE_OK); + xasprintf(&sc_return_code.output, "HTTP return code: %d", + curl_state.status_line->http_code); + if (httpReturnCode >= 600 || httpReturnCode < 100) { - die(STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), - curl_state.status_line->http_code, curl_state.status_line->msg); - /* server errors result in a critical state */ - } else if (httpReturnCode >= 500) { - result = STATE_CRITICAL; + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL); + xasprintf(&sc_return_code.output, _("Invalid Status (%d, %.40s)"), + curl_state.status_line->http_code, curl_state.status_line->msg); + mp_add_subcheck_to_subcheck(&sc_result, sc_return_code); + return sc_result; + } + + // server errors result in a critical state + if (httpReturnCode >= 500) { + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_CRITICAL); /* client errors result in a warning state */ } else if (httpReturnCode >= 400) { - result = STATE_WARNING; + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_WARNING); /* check redirected page if specified */ } else if (httpReturnCode >= 300) { if (config.on_redirect_dependent) { if (config.followmethod == FOLLOW_LIBCURL) { httpReturnCode = curl_state.status_line->http_code; + handle_curl_option_return_code( + curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), + "CURLINFO_REDIRECT_COUNT"); + + if (verbose >= 2) { + printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + } + + mp_subcheck sc_redir_depth = mp_subcheck_init(); + if (redir_depth > config.max_depth) { + xasprintf(&sc_redir_depth.output, + "maximum redirection depth %d exceeded in libcurl", + config.max_depth); + sc_redir_depth = mp_set_subcheck_state(sc_redir_depth, STATE_CRITICAL); + mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); + return sc_result; + } + xasprintf(&sc_redir_depth.output, "redirection depth %d (of a maximum %d)", + redir_depth, config.max_depth); + mp_add_subcheck_to_subcheck(&sc_result, sc_redir_depth); + } else { /* old check_http style redirection, if we come * back here, we are in the same status as with @@ -451,54 +470,53 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat redir_wrapper redir_result = redir(curl_state.header_buf, config, redir_depth, workingState); cleanup(curl_state); - check_http(config, redir_result.working_state, redir_result.redir_depth); + mp_subcheck sc_redir = + check_http(config, redir_result.working_state, redir_result.redir_depth); + mp_add_subcheck_to_subcheck(&sc_result, sc_redir); + + return sc_result; } } else { /* this is a specific code in the command line to * be returned when a redirection is encountered */ + sc_return_code = + mp_set_subcheck_state(sc_return_code, config.on_redirect_result_state); } - result = max_state_alt(config.on_redirect_result_state, result); - /* all other codes are considered ok */ } else { - result = STATE_OK; - } - } - - /* libcurl redirection internally, handle error states here */ - if (config.followmethod == FOLLOW_LIBCURL) { - handle_curl_option_return_code( - curl_easy_getinfo(curl_state.curl, CURLINFO_REDIRECT_COUNT, &redir_depth), - "CURLINFO_REDIRECT_COUNT"); - - if (verbose >= 2) { - printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth); + sc_return_code = mp_set_subcheck_state(sc_return_code, STATE_OK); } - if (redir_depth > config.max_depth) { - snprintf(msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl", - config.max_depth); - die(STATE_WARNING, "HTTP WARNING - %s", msg); - } + mp_add_subcheck_to_subcheck(&sc_result, sc_return_code); } /* check status codes, set exit status accordingly */ if (curl_state.status_line->http_code != httpReturnCode) { - die(STATE_CRITICAL, _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), - string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), - curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); + mp_subcheck sc_http_return_code_sanity = mp_subcheck_init(); + sc_http_return_code_sanity = + mp_set_subcheck_state(sc_http_return_code_sanity, STATE_CRITICAL); + xasprintf(&sc_http_return_code_sanity.output, + _("HTTP CRITICAL %s %d %s - different HTTP codes (cUrl has %ld)\n"), + string_statuscode(curl_state.status_line->http_major, + curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, httpReturnCode); + + mp_add_subcheck_to_subcheck(&sc_result, sc_http_return_code_sanity); + return sc_result; } if (config.maximum_age >= 0) { - result = max_state_alt( - check_document_dates(curl_state.header_buf, msg, config.maximum_age), result); + mp_subcheck sc_max_age = check_document_dates(curl_state.header_buf, config.maximum_age); + mp_add_subcheck_to_subcheck(&sc_result, sc_max_age); } /* Page and Header content checks go here */ - if (strlen(config.header_expect)) { - if (!strstr(curl_state.header_buf->buf, config.header_expect)) { + mp_subcheck sc_header_expect = mp_subcheck_init(); + sc_header_expect = mp_set_subcheck_default_state(sc_header_expect, STATE_OK); + xasprintf(&sc_header_expect.output, "Expect %s in header", config.header_expect); + if (!strstr(curl_state.header_buf->buf, config.header_expect)) { char output_header_search[30] = ""; strncpy(&output_header_search[0], config.header_expect, sizeof(output_header_search)); @@ -506,22 +524,23 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat bcopy("...", &output_header_search[sizeof(output_header_search) - 4], 4); } - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), - msg, output_header_search, workingState.use_ssl ? "https" : "http", - workingState.host_name ? workingState.host_name : workingState.server_address, - workingState.serverPort, workingState.server_url); - - strcpy(msg, tmp); + xasprintf(&sc_header_expect.output, _("header '%s' not found on '%s://%s:%d%s', "), + output_header_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); - result = STATE_CRITICAL; + sc_header_expect = mp_set_subcheck_state(sc_header_expect, STATE_CRITICAL); } + + mp_add_subcheck_to_subcheck(&sc_result, sc_header_expect); } if (strlen(config.string_expect)) { - if (!strstr(curl_state.body_buf->buf, config.string_expect)) { + mp_subcheck sc_string_expect = mp_subcheck_init(); + sc_string_expect = mp_set_subcheck_default_state(sc_string_expect, STATE_OK); + xasprintf(&sc_string_expect.output, "Expect string \"%s\" in body", config.string_expect); + if (!strstr(curl_state.body_buf->buf, config.string_expect)) { char output_string_search[30] = ""; strncpy(&output_string_search[0], config.string_expect, sizeof(output_string_search)); @@ -529,93 +548,86 @@ mp_state_enum check_http(const check_curl_config config, check_curl_working_stat bcopy("...", &output_string_search[sizeof(output_string_search) - 4], 4); } - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), - msg, output_string_search, workingState.use_ssl ? "https" : "http", - workingState.host_name ? workingState.host_name : workingState.server_address, - workingState.serverPort, workingState.server_url); + xasprintf(&sc_string_expect.output, _("string '%s' not found on '%s://%s:%d%s', "), + output_string_search, workingState.use_ssl ? "https" : "http", + workingState.host_name ? workingState.host_name : workingState.server_address, + workingState.serverPort, workingState.server_url); - strcpy(msg, tmp); - - result = STATE_CRITICAL; + sc_string_expect = mp_set_subcheck_state(sc_string_expect, STATE_CRITICAL); } + + mp_add_subcheck_to_subcheck(&sc_result, sc_string_expect); } if (strlen(config.regexp)) { + mp_subcheck sc_body_regex = mp_subcheck_init(); + xasprintf(&sc_body_regex.output, "Regex \"%s\" in body matched", config.regexp); regmatch_t pmatch[REGS]; + int errcode = regexec(&config.compiled_regex, curl_state.body_buf->buf, REGS, pmatch, 0); - if ((errcode == 0 && !config.invert_regex) || - (errcode == REG_NOMATCH && config.invert_regex)) { - /* OK - No-op to avoid changing the logic around it */ - result = max_state_alt(STATE_OK, result); - } else if ((errcode == REG_NOMATCH && !config.invert_regex) || - (errcode == 0 && config.invert_regex)) { - if (!config.invert_regex) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg); - strcpy(msg, tmp); + if (errcode == 0) { + // got a match + if (config.invert_regex) { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } else { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg); - strcpy(msg, tmp); + sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); } - result = config.state_regex; } else { - regerror(errcode, &config.compiled_regex, errbuf, MAX_INPUT_BUFFER); - - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf); - strcpy(msg, tmp); - result = STATE_UNKNOWN; + xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); + // got no match + if (config.invert_regex) { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); + } else { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); + } } - } - /* make sure the page is of an appropriate size */ - if ((config.max_page_len > 0) && (page_len > config.max_page_len)) { - char tmp[DEFAULT_BUFFER_SIZE]; + mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex); + } - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too large, "), msg, page_len); + // size a.k.a. page length + mp_perfdata pd_page_length = perfdata_init(); + size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); + mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len); + pd_page_length.value = pd_val_page_length; + pd_page_length.label = "size"; + pd_page_length.uom = "B"; + pd_page_length.min = mp_create_pd_value(0); + pd_page_length.warn = config.page_length_limits; + pd_page_length.warn_present = true; - strcpy(msg, tmp); + /* make sure the page is of an appropriate size */ + if (config.page_length_limits_is_set) { + mp_thresholds page_length_threshold = mp_thresholds_init(); + page_length_threshold.warning = config.page_length_limits; + page_length_threshold.warning_is_set = true; - result = max_state_alt(STATE_WARNING, result); + pd_page_length = mp_pd_set_thresholds(pd_page_length, page_length_threshold); - } else if ((config.min_page_len > 0) && (page_len < config.min_page_len)) { - char tmp[DEFAULT_BUFFER_SIZE]; + mp_subcheck sc_page_length = mp_subcheck_init(); - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%spage size %zu too small, "), msg, page_len); - strcpy(msg, tmp); - result = max_state_alt(STATE_WARNING, result); - } + mp_add_perfdata_to_subcheck(&sc_page_length, pd_page_length); - /* -w, -c: check warning and critical level */ - result = max_state_alt(get_status(total_time, config.thlds), result); + mp_state_enum tmp_state = mp_get_pd_status(pd_page_length); + sc_page_length = mp_set_subcheck_state(sc_page_length, tmp_state); - /* Cut-off trailing characters */ - if (strlen(msg) >= 2) { - if (msg[strlen(msg) - 2] == ',') { - msg[strlen(msg) - 2] = '\0'; - } else { - msg[strlen(msg) - 3] = '\0'; + switch (tmp_state) { + case STATE_CRITICAL: + case STATE_WARNING: + xasprintf(&sc_page_length.output, _("page size %zu violates threshold"), page_len); + break; + case STATE_OK: + xasprintf(&sc_page_length.output, _("page size %zu is OK"), page_len); + break; + default: + assert(false); } + + mp_add_subcheck_to_subcheck(&sc_result, sc_page_length); } - /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), - * msg); */ - die((int)max_state_alt(result, result_ssl), - "HTTP %s: %s %d %s%s%s - %zu bytes in %.3f second response time %s|%s\n%s%s", - state_text(result), - string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), - curl_state.status_line->http_code, curl_state.status_line->msg, strlen(msg) > 0 ? " - " : "", - msg, page_len, total_time, (config.display_html ? "" : ""), perfstring, - (config.show_body ? curl_state.body_buf->buf : ""), (config.show_body ? "\n" : "")); - - return max_state_alt(result, result_ssl); + return sc_result; } int uri_strcmp(const UriTextRangeA range, const char *stringToCompare) { @@ -895,8 +907,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } int option = 0; - char *warning_thresholds = NULL; - char *critical_thresholds = NULL; int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE; bool specify_port = false; bool enable_tls = false; @@ -931,11 +941,22 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } break; case 'c': /* critical time threshold */ - critical_thresholds = optarg; - break; + { + mp_range_parsed critical_range = mp_parse_range_string(optarg); + if (critical_range.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse critical threshold: %s", optarg); + } + result.config.thlds = mp_thresholds_set_crit(result.config.thlds, critical_range.range); + } break; case 'w': /* warning time threshold */ - warning_thresholds = optarg; - break; + { + mp_range_parsed warning_range = mp_parse_range_string(optarg); + + if (warning_range.error != MP_PARSING_SUCCES) { + die(STATE_UNKNOWN, "failed to parse warning threshold: %s", optarg); + } + result.config.thlds = mp_thresholds_set_warn(result.config.thlds, warning_range.range); + } break; case 'H': /* virtual host */ result.config.initial_config.host_name = strdup(optarg); char *tmp_string; @@ -1207,27 +1228,14 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { break; case 'm': /* min_page_length */ { - char *tmp; - if (strchr(optarg, ':') != (char *)NULL) { - /* range, so get two values, min:max */ - tmp = strtok(optarg, ":"); - if (tmp == NULL) { - printf("Bad format: try \"-m min:max\"\n"); - exit(STATE_WARNING); - } else { - result.config.min_page_len = atol(tmp); - } + mp_range_parsed foo = mp_parse_range_string(optarg); - tmp = strtok(NULL, ":"); - if (tmp == NULL) { - printf("Bad format: try \"-m min:max\"\n"); - exit(STATE_WARNING); - } else { - result.config.max_page_len = atol(tmp); - } - } else { - result.config.min_page_len = atol(optarg); + if (foo.error != MP_PARSING_SUCCES) { + die(STATE_CRITICAL, "failed to parse page size limits: %s", optarg); } + + result.config.page_length_limits = foo.range; + result.config.page_length_limits_is_set = true; break; } case 'N': /* no-body */ @@ -1401,16 +1409,6 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { } } - set_thresholds(&result.config.thlds, warning_thresholds, critical_thresholds); - - if (critical_thresholds && - result.config.thlds->critical->end > (double)result.config.curl_config.socket_timeout) { - result.config.curl_config.socket_timeout = (int)result.config.thlds->critical->end + 1; - } - if (verbose >= 2) { - printf("* Socket timeout set to %ld seconds\n", result.config.curl_config.socket_timeout); - } - if (result.config.initial_config.http_method == NULL) { result.config.initial_config.http_method = strdup("GET"); } diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index e33f2aa9..49949ccb 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -5,9 +5,12 @@ #include #include #include "../utils.h" +#include "check_curl.d/config.h" +#include "output.h" +#include "perfdata.h" +#include "states.h" extern int verbose; -char msg[DEFAULT_BUFFER_SIZE]; char errbuf[MAX_INPUT_BUFFER]; bool is_openssl_callback = false; bool add_sslctx_verify_fun = false; @@ -127,10 +130,9 @@ check_curl_configure_curl(const check_curl_static_curl_config config, int res; if ((res = lookup_host(working_state.server_address, addrstr, DEFAULT_BUFFER_SIZE / 2, config.sin_family)) != 0) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), - working_state.server_address, res, gai_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + die(STATE_CRITICAL, + _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), + working_state.server_address, res, gai_strerror(res)); } snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, working_state.serverPort, addrstr); @@ -154,12 +156,7 @@ check_curl_configure_curl(const check_curl_static_curl_config config, } /* compose URL: use the address we want to connect to, set Host: header later */ - char url[DEFAULT_BUFFER_SIZE]; - snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", working_state.use_ssl ? "https" : "http", - (working_state.use_ssl & (working_state.host_name != NULL)) - ? working_state.host_name - : working_state.server_address, - working_state.serverPort, working_state.server_url); + char *url = fmt_url(working_state); if (verbose >= 1) { printf("* curl CURLOPT_URL: %s\n", url); @@ -167,6 +164,8 @@ check_curl_configure_curl(const check_curl_static_curl_config config, handle_curl_option_return_code(curl_easy_setopt(result.curl_state.curl, CURLOPT_URL, url), "CURLOPT_URL"); + free(url); + /* extract proxy information for legacy proxy https requests */ if (!strcmp(working_state.http_method, "CONNECT") || strstr(working_state.server_url, "http") == working_state.server_url) { @@ -548,10 +547,8 @@ check_curl_configure_curl(const check_curl_static_curl_config config, void handle_curl_option_return_code(CURLcode res, const char *option) { if (res != CURLE_OK) { - snprintf(msg, DEFAULT_BUFFER_SIZE, - _("Error while setting cURL option '%s': cURL returned %d - %s"), option, res, - curl_easy_strerror(res)); - die(STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + die(STATE_CRITICAL, _("Error while setting cURL option '%s': cURL returned %d - %s"), + option, res, curl_easy_strerror(res)); } } @@ -618,9 +615,9 @@ check_curl_config check_curl_config_init() { .continue_after_check_cert = false, .days_till_exp_warn = 0, .days_till_exp_crit = 0, - .thlds = NULL, - .min_page_len = 0, - .max_page_len = 0, + .thlds = mp_thresholds_init(), + .page_length_limits = mp_range_init(), + .page_length_limits_is_set = false, .server_expect = { .string = HTTP_EXPECT, @@ -729,9 +726,7 @@ size_t get_content_length(const curlhelp_write_curlbuf *header_buf, return header_buf->buflen + body_buf->buflen; } -mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, - const char msg[static DEFAULT_BUFFER_SIZE], - const int maximum_age) { +mp_subcheck check_document_dates(const curlhelp_write_curlbuf *header_buf, const int maximum_age) { struct phr_header headers[255]; size_t nof_headers = 255; curlhelp_statusline status_line; @@ -747,73 +742,54 @@ mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, char *server_date = get_header_value(headers, nof_headers, "date"); char *document_date = get_header_value(headers, nof_headers, "last-modified"); - mp_state_enum date_result = STATE_OK; + mp_subcheck sc_document_dates = mp_subcheck_init(); if (!server_date || !*server_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), msg); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_UNKNOWN, date_result); - + xasprintf(&sc_document_dates.output, _("Server date unknown")); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_UNKNOWN); } else if (!document_date || !*document_date) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), msg); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); - + xasprintf(&sc_document_dates.output, _("Document modification date unknown, ")); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else { time_t srv_data = curl_getdate(server_date, NULL); time_t doc_data = curl_getdate(document_date, NULL); + if (verbose >= 2) { printf("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data); } - if (srv_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), msg, - server_date); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); + if (srv_data <= 0) { + xasprintf(&sc_document_dates.output, _("Server date \"%100s\" unparsable"), + server_date); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else if (doc_data <= 0) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), msg, - document_date); - strcpy(msg, tmp); - date_result = max_state_alt(STATE_CRITICAL, date_result); + xasprintf(&sc_document_dates.output, _("Document date \"%100s\" unparsable"), + document_date); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else if (doc_data > srv_data + 30) { - char tmp[DEFAULT_BUFFER_SIZE]; - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), msg, - (int)doc_data - (int)srv_data); - strcpy(msg, tmp); + xasprintf(&sc_document_dates.output, _("Document is %d seconds in the future"), + (int)doc_data - (int)srv_data); - date_result = max_state_alt(STATE_CRITICAL, date_result); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else if (doc_data < srv_data - maximum_age) { time_t last_modified = (srv_data - doc_data); - if (last_modified > (60 * 60 * 24 * 2)) { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), msg, - ((float)last_modified) / (60 * 60 * 24)); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); + if (last_modified > (60 * 60 * 24 * 2)) { // two days hardcoded? + xasprintf(&sc_document_dates.output, _("Last modified %.1f days ago"), + ((float)last_modified) / (60 * 60 * 24)); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } else { - char tmp[DEFAULT_BUFFER_SIZE]; - - snprintf(tmp, DEFAULT_BUFFER_SIZE, _("%sLast modified %ld:%02ld:%02ld ago, "), msg, - last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); - strcpy(msg, tmp); - - date_result = max_state_alt(STATE_CRITICAL, date_result); + xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_CRITICAL); } + } else { + // TODO is this the OK case? + time_t last_modified = (srv_data - doc_data); + xasprintf(&sc_document_dates.output, _("Last modified %ld:%02ld:%02ld ago"), + last_modified / (60 * 60), (last_modified / 60) % 60, last_modified % 60); + sc_document_dates = mp_set_subcheck_state(sc_document_dates, STATE_OK); } } @@ -824,7 +800,7 @@ mp_state_enum check_document_dates(const curlhelp_write_curlbuf *header_buf, free(document_date); } - return date_result; + return sc_document_dates; } void curlhelp_free_statusline(curlhelp_statusline *status_line) { free(status_line->first_line); } @@ -1172,46 +1148,117 @@ char *string_statuscode(int major, int minor) { return buf; } -char *perfd_time(double elapsed_time, thresholds *thlds, long socket_timeout) { - return fperfdata("time", elapsed_time, "s", (thlds->warning != NULL), - thlds->warning ? thlds->warning->end : 0, (thlds->critical != NULL), - thlds->critical ? thlds->critical->end : 0, true, 0, true, socket_timeout); +/* check whether a file exists */ +void test_file(char *path) { + if (access(path, R_OK) == 0) { + return; + } + usage2(_("file does not exist or is not readable"), path); } -char *perfd_time_connect(double elapsed_time_connect, long socket_timeout) { - return fperfdata("time_connect", elapsed_time_connect, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} +mp_subcheck np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit); -char *perfd_time_ssl(double elapsed_time_ssl, long socket_timeout) { - return fperfdata("time_ssl", elapsed_time_ssl, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} +mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, + int crit_days_till_exp) { + mp_subcheck sc_cert_result = mp_subcheck_init(); + sc_cert_result = mp_set_subcheck_default_state(sc_cert_result, STATE_OK); -char *perfd_time_headers(double elapsed_time_headers, long socket_timeout) { - return fperfdata("time_headers", elapsed_time_headers, "s", false, 0, false, 0, false, 0, true, - socket_timeout); -} +#ifdef LIBCURL_FEATURE_SSL + if (is_openssl_callback) { +# ifdef USE_OPENSSL + /* check certificate with OpenSSL functions, curl has been built against OpenSSL + * and we actually have OpenSSL in the monitoring tools + */ + return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); +# else /* USE_OPENSSL */ + xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " + "callback used and not linked against OpenSSL\n"); + mp_set_subcheck_state(result, STATE_CRITICAL); +# endif /* USE_OPENSSL */ + } else { + struct curl_slist *slist; + + cert_ptr_union cert_ptr = {0}; + cert_ptr.to_info = NULL; + CURLcode res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &cert_ptr.to_info); + if (!res && cert_ptr.to_info) { +# ifdef USE_OPENSSL + /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert + * parsing We only check the first certificate and assume it's the one of + * the server + */ + char *raw_cert = NULL; + bool got_first_cert = false; + for (int i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { + if (got_first_cert) { + break; + } + + for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { + if (verbose >= 2) { + printf("%d ** %s\n", i, slist->data); + } + if (strncmp(slist->data, "Cert:", 5) == 0) { + raw_cert = &slist->data[5]; + got_first_cert = true; + break; + } + } + } -char *perfd_time_firstbyte(double elapsed_time_firstbyte, long socket_timeout) { - return fperfdata("time_firstbyte", elapsed_time_firstbyte, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} + if (!raw_cert) { -char *perfd_time_transfer(double elapsed_time_transfer, long socket_timeout) { - return fperfdata("time_transfer", elapsed_time_transfer, "s", false, 0, false, 0, false, 0, - true, socket_timeout); -} + xasprintf(&sc_cert_result.output, + _("Cannot retrieve certificates from CERTINFO information - " + "certificate data was empty")); + sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); + return sc_cert_result; + } + + BIO *cert_BIO = BIO_new(BIO_s_mem()); + BIO_write(cert_BIO, raw_cert, (int)strlen(raw_cert)); + + cert = PEM_read_bio_X509(cert_BIO, NULL, NULL, NULL); + if (!cert) { + xasprintf(&sc_cert_result.output, + _("Cannot read certificate from CERTINFO information - BIO error")); + sc_cert_result = mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); + return sc_cert_result; + } -char *perfd_size(size_t page_len, int min_page_len) { - return perfdata("size", page_len, "B", (min_page_len > 0), min_page_len, (min_page_len > 0), 0, - true, 0, false, 0); + BIO_free(cert_BIO); + return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); +# else /* USE_OPENSSL */ + /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our + * disposal, so we use the libcurl CURLINFO data + */ + return net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, + days_till_exp_crit); +# endif /* USE_OPENSSL */ + } else { + xasprintf(&sc_cert_result.output, + _("Cannot retrieve certificates - cURL returned %d - %s"), res, + curl_easy_strerror(res)); + mp_set_subcheck_state(sc_cert_result, STATE_CRITICAL); + } + } +#endif /* LIBCURL_FEATURE_SSL */ + + return sc_cert_result; } -/* check whether a file exists */ -void test_file(char *path) { - if (access(path, R_OK) == 0) { - return; +char *fmt_url(check_curl_working_state workingState) { + char *url = calloc(DEFAULT_BUFFER_SIZE, sizeof(char)); + if (url == NULL) { + die(STATE_UNKNOWN, "memory allocation failed"); } - usage2(_("file does not exist or is not readable"), path); + + snprintf(url, DEFAULT_BUFFER_SIZE, "%s://%s:%d%s", workingState.use_ssl ? "https" : "http", + (workingState.use_ssl & (workingState.host_name != NULL)) + ? workingState.host_name + : workingState.server_address, + workingState.serverPort, workingState.server_url); + + return url; } diff --git a/plugins/check_curl.d/check_curl_helpers.h b/plugins/check_curl.d/check_curl_helpers.h index 0f43ab90..87e45a9d 100644 --- a/plugins/check_curl.d/check_curl_helpers.h +++ b/plugins/check_curl.d/check_curl_helpers.h @@ -1,7 +1,11 @@ #include "./config.h" #include #include "../picohttpparser/picohttpparser.h" -// #include "curl/easy.h" +#include "output.h" + +#if defined(HAVE_SSL) && defined(USE_OPENSSL) +# include +#endif /* for buffers for header and body */ typedef struct { @@ -99,8 +103,8 @@ int curlhelp_parse_statusline(const char * /*buf*/, curlhelp_statusline * /*stat void curlhelp_free_statusline(curlhelp_statusline * /*status_line*/); char *get_header_value(const struct phr_header *headers, size_t nof_headers, const char *header); -mp_state_enum check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, - const char msg[static DEFAULT_BUFFER_SIZE], int /*maximum_age*/); +mp_subcheck check_document_dates(const curlhelp_write_curlbuf * /*header_buf*/, + int /*maximum_age*/); size_t get_content_length(const curlhelp_write_curlbuf *header_buf, const curlhelp_write_curlbuf *body_buf); int lookup_host(const char *host, char *buf, size_t buflen, sa_family_t addr_family); @@ -114,12 +118,7 @@ void cleanup(check_curl_global_state global_state); bool expected_statuscode(const char *reply, const char *statuscodes); char *string_statuscode(int major, int minor); -char *perfd_time(double elapsed_time, thresholds * /*thlds*/, long /*socket_timeout*/); -char *perfd_time_connect(double elapsed_time_connect, long /*socket_timeout*/); -char *perfd_time_ssl(double elapsed_time_ssl, long /*socket_timeout*/); -char *perfd_time_firstbyte(double elapsed_time_firstbyte, long /*socket_timeout*/); -char *perfd_time_headers(double elapsed_time_headers, long /*socket_timeout*/); -char *perfd_time_transfer(double elapsed_time_transfer, long /*socket_timeout*/); -char *perfd_size(size_t page_len, int /*min_page_len*/); - void test_file(char *path); +mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, + int crit_days_till_exp); +char *fmt_url(check_curl_working_state workingState); diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index a4b1eecf..32399b8a 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -8,6 +8,7 @@ #include #include #include "curl/curl.h" +#include "perfdata.h" #include "regex.h" enum { @@ -93,9 +94,9 @@ typedef struct { bool continue_after_check_cert; int days_till_exp_warn; int days_till_exp_crit; - thresholds *thlds; - size_t min_page_len; - size_t max_page_len; + mp_thresholds thlds; + mp_range page_length_limits; + bool page_length_limits_is_set; struct { char string[MAX_INPUT_BUFFER]; bool is_present; -- cgit v1.2.3-74-g34f1 From 9afb5e2a6964aad149ca3ef0c175fcd510220505 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:28:49 +0200 Subject: check_curl: remove display-html option --- plugins/check_curl.c | 35 ++++++++++--------------------- plugins/check_curl.d/check_curl_helpers.c | 1 - plugins/check_curl.d/config.h | 1 - 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 722666dd..17fb5564 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -139,16 +139,6 @@ int main(int argc, char **argv) { const check_curl_config config = tmp_config.config; - if (config.display_html) { - printf("", - config.initial_config.use_ssl ? "https" : "http", - config.initial_config.host_name ? config.initial_config.host_name - : config.initial_config.server_address, - config.initial_config.virtualPort ? config.initial_config.virtualPort - : config.initial_config.serverPort, - config.initial_config.server_url); - } - check_curl_working_state working_state = config.initial_config; mp_check overall = mp_check_init(); @@ -672,8 +662,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } if (++redir_depth > config.max_depth) { - die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"), - config.max_depth, location, (config.display_html ? "" : "")); + die(STATE_WARNING, _("HTTP WARNING - maximum redirection depth %d exceeded - %s\n"), + config.max_depth, location); } UriParserStateA state; @@ -681,8 +671,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config state.uri = &uri; if (uriParseUriA(&state, location) != URI_SUCCESS) { if (state.errorCode == URI_ERROR_SYNTAX) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"), - location, (config.display_html ? "" : "")); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not parse redirect location '%s'\n"), + location); } else if (state.errorCode == URI_ERROR_MALLOC) { die(STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n")); } @@ -735,8 +725,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config } } if (new_port > MAX_PORT) { - die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"), MAX_PORT, - location, config.display_html ? "" : ""); + die(STATE_UNKNOWN, _("HTTP UNKNOWN - Redirection to port above %d - %s\n"), MAX_PORT, + location); } /* by RFC 7231 relative URLs in Location should be taken relative to @@ -772,9 +762,8 @@ redir_wrapper redir(curlhelp_write_curlbuf *header_buf, const check_curl_config !strncmp(working_state.host_name, new_host, MAX_IPV4_HOSTLENGTH)) && !strcmp(working_state.server_url, new_url)) { die(STATE_CRITICAL, - _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s%s\n"), - working_state.use_ssl ? "https" : "http", new_host, new_port, new_url, - (config.display_html ? "" : "")); + _("HTTP CRITICAL - redirection creates an infinite loop - %s://%s:%d%s\n"), + working_state.use_ssl ? "https" : "http", new_host, new_port, new_url); } /* set new values for redirected request */ @@ -1037,10 +1026,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { .http_opt_headers[result.config.curl_config.http_opt_headers_count - 1] = optarg; break; case 'L': /* show html link */ - result.config.display_html = true; - break; case 'n': /* do not show html link */ - result.config.display_html = false; + // HTML link related options are deprecated break; case 'C': /* Check SSL cert validity */ #ifndef LIBCURL_FEATURE_SSL @@ -1566,8 +1553,8 @@ void print_help(void) { printf(" %s\n", _("Print additional performance data")); printf(" %s\n", "-B, --show-body"); printf(" %s\n", _("Print body content below status line")); - printf(" %s\n", "-L, --link"); - printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); + // printf(" %s\n", "-L, --link"); + // printf(" %s\n", _("Wrap output in HTML link (obsoleted by urlize)")); printf(" %s\n", "-f, --onredirect="); printf(" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the")); printf(" %s\n", _("specified IP address. stickyport also ensures port stays the same.")); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index 49949ccb..f0a4d265 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -630,7 +630,6 @@ check_curl_config check_curl_config_init() { .show_extended_perfdata = false, .show_body = false, - .display_html = false, }; snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 32399b8a..6100af3e 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -108,7 +108,6 @@ typedef struct { bool show_extended_perfdata; bool show_body; - bool display_html; } check_curl_config; check_curl_config check_curl_config_init(); -- cgit v1.2.3-74-g34f1 From 6ae8ba911018571afddcf51c08e3d32f5efa3b5a Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:49:06 +0200 Subject: check_curl: test adaption and output adaption --- plugins/check_curl.c | 39 +++++++++++--------- plugins/t/check_curl.t | 4 +-- plugins/tests/check_curl.t | 89 +++++++++++++++++++++++----------------------- 3 files changed, 69 insertions(+), 63 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 17fb5564..89173808 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -238,7 +238,28 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state return sc_result; } - xasprintf(&sc_curl.output, "cURL performed query"); + /* get status line of answer, check sanity of HTTP code */ + if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { + sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); + /* we cannot know the major/minor version here for sure as we cannot parse the first + * line */ + xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line"); + return sc_result; + } + + curl_state.status_line_initialized = true; + + size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); + + double total_time; + handle_curl_option_return_code( + curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), + "CURLINFO_TOTAL_TIME"); + + xasprintf( + &sc_curl.output, "%s %d %s - %ld bytes in %.3f second response time", + string_statuscode(curl_state.status_line->http_major, curl_state.status_line->http_minor), + curl_state.status_line->http_code, curl_state.status_line->msg, page_len, total_time); sc_curl = mp_set_subcheck_state(sc_curl, STATE_OK); mp_add_subcheck_to_subcheck(&sc_result, sc_curl); @@ -264,10 +285,6 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state // total time the query took mp_perfdata pd_total_time = perfdata_init(); - double total_time; - handle_curl_option_return_code( - curl_easy_getinfo(curl_state.curl, CURLINFO_TOTAL_TIME, &total_time), - "CURLINFO_TOTAL_TIME"); mp_perfdata_value pd_val_total_time = mp_create_pd_value(total_time); pd_total_time.value = pd_val_total_time; pd_total_time = mp_pd_set_thresholds(pd_total_time, config.thlds); @@ -360,17 +377,6 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state return sc_result; } - /* get status line of answer, check sanity of HTTP code */ - if (curlhelp_parse_statusline(curl_state.header_buf->buf, curl_state.status_line) < 0) { - sc_result = mp_set_subcheck_state(sc_result, STATE_CRITICAL); - /* we cannot know the major/minor version here for sure as we cannot parse the first - * line */ - xasprintf(&sc_result.output, "HTTP/x.x unknown - Unparsable status line"); - return sc_result; - } - - curl_state.status_line_initialized = true; - /* get result code from cURL */ long httpReturnCode; handle_curl_option_return_code( @@ -578,7 +584,6 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state // size a.k.a. page length mp_perfdata pd_page_length = perfdata_init(); - size_t page_len = get_content_length(curl_state.header_buf, curl_state.body_buf); mp_perfdata_value pd_val_page_length = mp_create_pd_value(page_len); pd_page_length.value = pd_val_page_length; pd_page_length.label = "size"; diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t index 7a930a4e..7f1a2de0 100644 --- a/plugins/t/check_curl.t +++ b/plugins/t/check_curl.t @@ -18,7 +18,7 @@ BEGIN { } -my $successOutput = '/OK.*HTTP.*second/'; +my $successOutput = '/.*HTTP.*second/'; my $res; my $plugin = 'check_http'; @@ -63,7 +63,7 @@ $res = NPTest->testCmd( ); cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); # was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!) -like( $res->output, "/HTTP CRITICAL - Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); +like( $res->output, "/Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); $res = NPTest->testCmd( "./$plugin $hostname_invalid -wt 1 -ct 2" diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index eaa9f518..e66d0ba6 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t @@ -15,6 +15,7 @@ # Email Address []:devel@monitoring-plugins.org use strict; +use warnings; use Test::More; use NPTest; use FindBin qw($Bin); @@ -245,21 +246,21 @@ SKIP: { $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); - is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on $expiry.", "output ok" ); + like( $result->output, "/.*Certificate \'Monitoring Plugins\' will expire on $expiry.*", "output ok" ); $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); - like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); + like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" ); # Expired cert tests $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" ); - like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" ); + like( $result->output, '/.*Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\).*/', "output ok" ); $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); - is( $result->output, - 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.', + like( $result->output, + '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.*/', "output ok" ); } @@ -274,19 +275,19 @@ SKIP: { $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$port_http\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # http with virtual port (!= 80) $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host:$virtual_port\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # http with virtual port (80) $cmd = "./$plugin -H $virtual_host:80 -I 127.0.0.1 -p $port_http -u /virtual_port -r ^$virtual_host\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); } # and the same for SSL @@ -296,19 +297,19 @@ SKIP: { $cmd = "./$plugin -H $virtual_host -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$port_https\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # https with virtual port (!= 443) $cmd = "./$plugin -H $virtual_host:$virtual_port -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host:$virtual_port\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # https with virtual port (443) $cmd = "./$plugin -H $virtual_host:443 -I 127.0.0.1 -p $port_https --ssl -u /virtual_port -r ^$virtual_host\$"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); } @@ -321,165 +322,165 @@ sub run_common_tests { $result = NPTest->testCmd( "$command -u /file/root" ); is( $result->return_code, 0, "/file/root"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" ); $result = NPTest->testCmd( "$command -u /file/root -s Root" ); is( $result->return_code, 0, "/file/root search for string"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second.*/', "Output correct" ); $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" ); is( $result->return_code, 2, "Missing string check"); - like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); + like( $result->output, qr%string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" ); is( $result->return_code, 2, "Missing string check"); - like( $result->output, qr%HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); + like( $result->output, qr%string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location"); $result = NPTest->testCmd( "$command -u /header_check -d foo" ); is( $result->return_code, 0, "header_check search for string"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second.*/', "Output correct" ); $result = NPTest->testCmd( "$command -u /header_check -d bar" ); is( $result->return_code, 2, "Missing header string check"); - like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); + like( $result->output, qr%header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location"); $result = NPTest->testCmd( "$command -u /header_broken_check" ); is( $result->return_code, 0, "header_check search for string"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second/', "Output correct" ); + like( $result->output, '/.*HTTP/1.1 200 OK - 138 bytes in [\d\.]+ second.*/', "Output correct" ); my $cmd; $cmd = "$command -u /slow"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, "$cmd"); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $result->output =~ /in ([\d\.]+) second/; cmp_ok( $1, ">", 1, "Time is > 1 second" ); $cmd = "$command -u /statuscode/200"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/200 -e 200"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "200".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201 -e 201"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "201".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201 -e 200"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 2, $cmd); - like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output ); + like( $result->output, '/.*Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created.*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/200 -e 200,201,202"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/201 -e 200,201,202"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*Status line output matched "200,201,202".*/', "Output correct: ".$result->output ); $cmd = "$command -u /statuscode/203 -e 200,201,202"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 2, $cmd); - like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information/', "Output correct: ".$result->output ); + like( $result->output, '/.*Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information.*/', "Output correct: ".$result->output ); $cmd = "$command -j HEAD -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j POST -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j GET -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -P foo -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j DELETE -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 1, $cmd); - like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 405 Method Not Allowed.*/', "Output correct: ".$result->output ); $cmd = "$command -j foo -u /method"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 2, $cmd); - like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 501 Not Implemented.*/', "Output correct: ".$result->output ); $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # To confirm that the free doesn't segfault $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /redirect"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f follow -u /redirect"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f follow -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f sticky -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f stickyport -u /redirect -k 'follow: me'"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); $cmd = "$command -f follow -u /redirect_rel -s redirected"; $result = NPTest->testCmd( $cmd ); is( $result->return_code, 0, $cmd); - like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output ); + like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); # These tests may block # stickyport - on full urlS port is set back to 80 otherwise -- cgit v1.2.3-74-g34f1 From def42dc686736107f95d66fe8f35d78528b77779 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:22:08 +0200 Subject: Improve error message --- plugins/check_curl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 89173808..7cb9745f 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -230,9 +230,8 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - xasprintf(&sc_curl.output, - _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), - workingState.serverPort, res, errbuf[0] ? errbuf : curl_easy_strerror(res)); + xasprintf(&sc_curl.output, _("Error while performing connectiion: cURL returned %d - %s"), + res, errbuf[0] ? errbuf : curl_easy_strerror(res)); sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL); mp_add_subcheck_to_subcheck(&sc_result, sc_curl); return sc_result; -- cgit v1.2.3-74-g34f1 From 166ce184c67a95192de8ec7f862fd965ea672ba8 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:22:35 +0200 Subject: Fix regex matching --- plugins/check_curl.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 7cb9745f..f8adc3d9 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -564,18 +564,24 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state if (errcode == 0) { // got a match if (config.invert_regex) { - sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); - } else { sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); + } else { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } - } else { + } else if (errcode == REG_NOMATCH) { xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); // got no match if (config.invert_regex) { - sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); - } else { sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); + } else { + sc_body_regex = mp_set_subcheck_state(sc_body_regex, config.state_regex); } + } else { + // error in regexec + char error_buffer[DEFAULT_BUFFER_SIZE]; + regerror(errcode, &config.compiled_regex, &error_buffer[0], DEFAULT_BUFFER_SIZE); + xasprintf(&sc_body_regex.output, "regexec error: %s", error_buffer); + sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_UNKNOWN); } mp_add_subcheck_to_subcheck(&sc_result, sc_body_regex); -- cgit v1.2.3-74-g34f1 From 0bf5fdf41e0ce5def14d0c7fda93b57652a9bbb3 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:56:46 +0200 Subject: check_curl: finish after cert without continue param --- plugins/check_curl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index f8adc3d9..b6e2e10c 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -273,7 +273,7 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state mp_add_subcheck_to_subcheck(&sc_result, sc_certificate); if (!config.continue_after_check_cert) { - // TODO finish here then + return sc_result; } } #endif -- cgit v1.2.3-74-g34f1 From c15d12cbd5008de74dccd6f716e418feffed5560 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:57:08 +0200 Subject: check_curl: fix perfdata label --- plugins/check_curl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b6e2e10c..680ecef7 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -307,7 +307,7 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state mp_perfdata_value pd_val_time_connect = mp_create_pd_value(time_connect); pd_time_connect.value = pd_val_time_connect; - pd_time_connect.label = "time"; + pd_time_connect.label = "time_connect"; pd_time_connect.uom = "s"; pd_time_connect = mp_set_pd_max_value( pd_time_connect, mp_create_pd_value(config.curl_config.socket_timeout)); -- cgit v1.2.3-74-g34f1 From f5f4a021a2760d553e3e4cdedd291eb815750369 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:57:40 +0200 Subject: Add new cert check function --- plugins/check_curl.c | 3 +- plugins/netutils.h | 3 +- plugins/sslutils.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 680ecef7..b1021045 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -569,8 +569,9 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } } else if (errcode == REG_NOMATCH) { - xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); // got no match + xasprintf(&sc_body_regex.output, "%s not", sc_body_regex.output); + if (config.invert_regex) { sc_body_regex = mp_set_subcheck_state(sc_body_regex, STATE_OK); } else { diff --git a/plugins/netutils.h b/plugins/netutils.h index c53b3cef..6adb8e01 100644 --- a/plugins/netutils.h +++ b/plugins/netutils.h @@ -32,6 +32,7 @@ #define _NETUTILS_H_ #include "common.h" +#include "output.h" #include "states.h" #include "utils.h" #include @@ -114,6 +115,6 @@ void np_net_ssl_cleanup(); int np_net_ssl_write(const void *buf, int num); int np_net_ssl_read(void *buf, int num); mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); +mp_subcheck mp_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit); #endif /* HAVE_SSL */ - #endif /* _NETUTILS_H_ */ diff --git a/plugins/sslutils.c b/plugins/sslutils.c index bea1307f..3ce6afed 100644 --- a/plugins/sslutils.c +++ b/plugins/sslutils.c @@ -26,6 +26,7 @@ * *****************************************************************************/ +#include "output.h" #define MAX_CN_LENGTH 256 #include "common.h" #include "netutils.h" @@ -322,4 +323,138 @@ mp_state_enum np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_cr # endif /* USE_OPENSSL */ } +mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, + int days_till_exp_crit) { + mp_subcheck sc_cert = mp_subcheck_init(); +# ifdef USE_OPENSSL + if (!certificate) { + xasprintf(&sc_cert.output, _("No server certificate present to inspect")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + + /* Extract CN from certificate subject */ + X509_NAME *subj = X509_get_subject_name(certificate); + + if (!subj) { + xasprintf(&sc_cert.output, _("Cannot retrieve certificate subject")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + + char commonName[MAX_CN_LENGTH] = ""; + int cnlen = X509_NAME_get_text_by_NID(subj, NID_commonName, commonName, sizeof(commonName)); + if (cnlen == -1) { + strcpy(commonName, _("Unknown CN")); + } + + /* Retrieve timestamp of certificate */ + ASN1_STRING *expiry_timestamp = X509_get_notAfter(certificate); + + int offset = 0; + struct tm stamp = {}; + /* Generate tm structure to process timestamp */ + if (expiry_timestamp->type == V_ASN1_UTCTIME) { + if (expiry_timestamp->length < 10) { + xasprintf(&sc_cert.output, _("Wrong time format in certificate")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + + stamp.tm_year = (expiry_timestamp->data[0] - '0') * 10 + (expiry_timestamp->data[1] - '0'); + if (stamp.tm_year < 50) { + stamp.tm_year += 100; + } + + offset = 0; + } else { + if (expiry_timestamp->length < 12) { + xasprintf(&sc_cert.output, _("Wrong time format in certificate")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + return sc_cert; + } + stamp.tm_year = (expiry_timestamp->data[0] - '0') * 1000 + + (expiry_timestamp->data[1] - '0') * 100 + + (expiry_timestamp->data[2] - '0') * 10 + (expiry_timestamp->data[3] - '0'); + stamp.tm_year -= 1900; + offset = 2; + } + + stamp.tm_mon = (expiry_timestamp->data[2 + offset] - '0') * 10 + + (expiry_timestamp->data[3 + offset] - '0') - 1; + stamp.tm_mday = (expiry_timestamp->data[4 + offset] - '0') * 10 + + (expiry_timestamp->data[5 + offset] - '0'); + stamp.tm_hour = (expiry_timestamp->data[6 + offset] - '0') * 10 + + (expiry_timestamp->data[7 + offset] - '0'); + stamp.tm_min = (expiry_timestamp->data[8 + offset] - '0') * 10 + + (expiry_timestamp->data[9 + offset] - '0'); + stamp.tm_sec = (expiry_timestamp->data[10 + offset] - '0') * 10 + + (expiry_timestamp->data[11 + offset] - '0'); + stamp.tm_isdst = -1; + + time_t tm_t = timegm(&stamp); + double time_left = difftime(tm_t, time(NULL)); + int days_left = (int)(time_left / 86400); + char *timeZone = getenv("TZ"); + setenv("TZ", "GMT", 1); + tzset(); + + char timestamp[50] = ""; + strftime(timestamp, 50, "%c %z", localtime(&tm_t)); + if (timeZone) { + setenv("TZ", timeZone, 1); + } else { + unsetenv("TZ"); + } + + tzset(); + + int time_remaining; + if (days_left > 0 && days_left <= days_till_exp_warn) { + xasprintf(&sc_cert.output, _("Certificate '%s' expires in %d day(s) (%s)"), commonName, + days_left, timestamp); + if (days_left > days_till_exp_crit) { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + } else { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } + } else if (days_left == 0 && time_left > 0) { + if (time_left >= 3600) { + time_remaining = (int)time_left / 3600; + } else { + time_remaining = (int)time_left / 60; + } + + xasprintf(&sc_cert.output, _("Certificate '%s' expires in %u %s (%s)"), commonName, + time_remaining, time_left >= 3600 ? "hours" : "minutes", timestamp); + + if (days_left > days_till_exp_crit) { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + } else { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } + } else if (time_left < 0) { + xasprintf(&sc_cert.output, _("Certificate '%s' expired on %s"), commonName, timestamp); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } else if (days_left == 0) { + xasprintf(&sc_cert.output, _("Certificate '%s' just expired (%s)"), commonName, + timestamp); + if (days_left > days_till_exp_crit) { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + } else { + sc_cert = mp_set_subcheck_state(sc_cert, STATE_CRITICAL); + } + } else { + xasprintf(&sc_cert.output, _("Certificate '%s' will expire on %s"), commonName, + timestamp); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_OK); + } + X509_free(certificate); + return sc_cert; +# else /* ifndef USE_OPENSSL */ + xasprintf(&sc_cert.output, _("Plugin does not support checking certificates")); + sc_cert = mp_set_subcheck_state(sc_cert, STATE_WARNING); + return sc_cert; +# endif /* USE_OPENSSL */ +} #endif /* HAVE_SSL */ -- cgit v1.2.3-74-g34f1 From afa81204dc2f7cadb63c23aa47a0262dbf654139 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:58:07 +0200 Subject: check_curl: use new cert check function --- plugins/check_curl.d/check_curl_helpers.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index f0a4d265..ab43a1e1 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -134,10 +134,12 @@ check_curl_configure_curl(const check_curl_static_curl_config config, _("Unable to lookup IP address for '%s': getaddrinfo returned %d - %s"), working_state.server_address, res, gai_strerror(res)); } + snprintf(dnscache, DEFAULT_BUFFER_SIZE, "%s:%d:%s", working_state.host_name, working_state.serverPort, addrstr); result.curl_state.host = curl_slist_append(NULL, dnscache); curl_easy_setopt(result.curl_state.curl, CURLOPT_RESOLVE, result.curl_state.host); + if (verbose >= 1) { printf("* curl CURLOPT_RESOLVE: %s\n", dnscache); } @@ -1155,7 +1157,7 @@ void test_file(char *path) { usage2(_("file does not exist or is not readable"), path); } -mp_subcheck np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, +mp_subcheck mp_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit); mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_till_exp, @@ -1169,7 +1171,7 @@ mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_ /* check certificate with OpenSSL functions, curl has been built against OpenSSL * and we actually have OpenSSL in the monitoring tools */ - return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); + return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); # else /* USE_OPENSSL */ xasprintf(&result.output, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL " "callback used and not linked against OpenSSL\n"); @@ -1227,7 +1229,7 @@ mp_subcheck check_curl_certificate_checks(CURL *curl, X509 *cert, int warn_days_ } BIO_free(cert_BIO); - return np_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); + return mp_net_ssl_check_certificate(cert, warn_days_till_exp, crit_days_till_exp); # else /* USE_OPENSSL */ /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our * disposal, so we use the libcurl CURLINFO data -- cgit v1.2.3-74-g34f1 From 404d52efb9b814013d5664019cab714253ac13ff Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:58:26 +0200 Subject: Fix/adapt tests --- plugins/t/check_curl.t | 43 +++++++++++++++++++++++++++---------------- plugins/tests/check_curl.t | 4 ++-- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t index 7f1a2de0..240c8f37 100644 --- a/plugins/t/check_curl.t +++ b/plugins/t/check_curl.t @@ -124,14 +124,14 @@ SKIP: { $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" ); cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); - like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); + like ( $res->output, "/matched not/", "Error message says 'matched not'"); $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" ); cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); - like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); + like ( $res->output, "/matched/", "Error message says 'matched'"); $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); cmp_ok( $res->return_code, "==", 0, "And also when not found"); @@ -151,63 +151,74 @@ SKIP: { $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" ); cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); - like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); + like ( $res->output, qr/Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); is( $res->return_code, 0, "Old syntax for cert checking okay" ); - is( $res->output, $saved_cert_output, "Same output as new syntax" ); + # deactived since different timings will change the output + # TODO compare without perfdata + # is( $res->output, $saved_cert_output, "Same output as new syntax" ); $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" ); is( $res->return_code, 0, "Updated syntax for cert checking okay" ); - is( $res->output, $saved_cert_output, "Same output as new syntax" ); + # deactived since different timings will change the output + # TODO compare without perfdata + # is( $res->output, $saved_cert_output, "Same output as new syntax" ); $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" ); - cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); + # deactived since different timings will change the output + # TODO compare without perfdata + # cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); - cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); + # deactived since different timings will change the output + # TODO compare without perfdata + # cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); # run some certificate checks with faketime SKIP: { skip "No faketime binary found", 12 if !$faketime; $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http"); - like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output"); + like($res->output, qr/Certificate '$host_tls_cert' will expire on/, "Catch cert output"); is( $res->return_code, 0, "Catch cert output exit code" ); + my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); if(!defined $year) { die("parsing date failed from: ".$res->output); } + my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11}; my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); + $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http"); - like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date"); + like($res->output, qr/Certificate '$host_tls_cert' just expired/, "Output on expire date"); is( $res->return_code, 2, "Output on expire date" ); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http"); - like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); + like($res->output, qr/Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); is( $res->return_code, 2, "cert expires in 1 second exit code" ); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http"); - like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); + like($res->output, qr/Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); is( $res->return_code, 2, "cert expires in 2 minutes exit code" ); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http"); - like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); + like($res->output, qr/Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); is( $res->return_code, 2, "cert expires in 2 hours exit code" ); $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http"); - like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output"); + like($res->output, qr/Certificate '$host_tls_cert' expired on/, "Certificate expired output"); is( $res->return_code, 2, "Certificate expired exit code" ); }; $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" ); - like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); - like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); + like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' ); + like ( $res->output, '/\'time_tls\'=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org -u /download.html -f follow" ); is( $res->return_code, 0, "Redirection based on location is okay"); $res = NPTest->testCmd( "./$plugin -H monitoring-plugins.org --extended-perfdata" ); - like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); + like ( $res->output, '/\'time_connect\'=[\d\.]+/', 'Extended Performance Data Output OK' ); } diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index e66d0ba6..52c5ad1c 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t @@ -246,7 +246,7 @@ SKIP: { $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); - like( $result->output, "/.*Certificate \'Monitoring Plugins\' will expire on $expiry.*", "output ok" ); + like( $result->output, '/.*Certificate \'Monitoring Plugins\' will expire on ' . quotemeta($expiry) . '.*/', "output ok" ); $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); @@ -260,7 +260,7 @@ SKIP: { $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); like( $result->output, - '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.*/', + '/.*Certificate \'Monitoring Plugins\' expired on Wed Jan\s+2 12:00:00 2008 \+0000.*/', "output ok" ); } -- cgit v1.2.3-74-g34f1 From c892db9ae16183fdda9c96b3d4b93c58355b34ac Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 02:02:04 +0200 Subject: Fix typos --- plugins/t/check_curl.t | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t index 240c8f37..5d1f1811 100644 --- a/plugins/t/check_curl.t +++ b/plugins/t/check_curl.t @@ -155,23 +155,23 @@ SKIP: { $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); is( $res->return_code, 0, "Old syntax for cert checking okay" ); - # deactived since different timings will change the output + # deactivated since different timings will change the output # TODO compare without perfdata # is( $res->output, $saved_cert_output, "Same output as new syntax" ); $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" ); is( $res->return_code, 0, "Updated syntax for cert checking okay" ); - # deactived since different timings will change the output + # deactivated since different timings will change the output # TODO compare without perfdata # is( $res->output, $saved_cert_output, "Same output as new syntax" ); $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" ); - # deactived since different timings will change the output + # deactivated since different timings will change the output # TODO compare without perfdata # cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" ); - # deactived since different timings will change the output + # deactivated since different timings will change the output # TODO compare without perfdata # cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); -- cgit v1.2.3-74-g34f1 From 43c6332322f47285576218429be6a41c03912b57 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 02:08:46 +0200 Subject: Fix typo in error message --- plugins/check_curl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b1021045..6d50568f 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -230,7 +230,7 @@ mp_subcheck check_http(const check_curl_config config, check_curl_working_state /* Curl errors, result in critical Nagios state */ if (res != CURLE_OK) { - xasprintf(&sc_curl.output, _("Error while performing connectiion: cURL returned %d - %s"), + xasprintf(&sc_curl.output, _("Error while performing connection: cURL returned %d - %s"), res, errbuf[0] ? errbuf : curl_easy_strerror(res)); sc_curl = mp_set_subcheck_state(sc_curl, STATE_CRITICAL); mp_add_subcheck_to_subcheck(&sc_result, sc_curl); -- cgit v1.2.3-74-g34f1 From c07dd02beefe225e077048166bc0fb6eb516bdf9 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 02:09:04 +0200 Subject: Adapt test to new error message --- plugins/t/check_curl.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t index 5d1f1811..2b2d81a8 100644 --- a/plugins/t/check_curl.t +++ b/plugins/t/check_curl.t @@ -63,7 +63,7 @@ $res = NPTest->testCmd( ); cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); # was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!) -like( $res->output, "/Invalid HTTP response received from host on port 80: cURL returned 28 - Connection timed out after/", "Output OK"); +like( $res->output, "/cURL returned 28 - Connection timed out after/", "Output OK"); $res = NPTest->testCmd( "./$plugin $hostname_invalid -wt 1 -ct 2" -- cgit v1.2.3-74-g34f1 From eca9eaf9f5771e417366bd3dac0cd463f405eb70 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 02:17:44 +0200 Subject: fix number of tests --- plugins/t/check_curl.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t index 2b2d81a8..2c2fafde 100644 --- a/plugins/t/check_curl.t +++ b/plugins/t/check_curl.t @@ -13,7 +13,7 @@ use vars qw($tests $has_ipv6); BEGIN { use NPTest; $has_ipv6 = NPTest::has_ipv6(); - $tests = $has_ipv6 ? 59 : 57; + $tests = $has_ipv6 ? 55 : 53; plan tests => $tests; } -- cgit v1.2.3-74-g34f1 From 5a2c1b2c3aeb99e7a703b0c5f6fe1a21d29b3be4 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 02:30:42 +0200 Subject: Add output formatting option --- plugins/check_curl.c | 22 +++++++++++++++++++++- plugins/check_curl.d/check_curl_helpers.c | 2 ++ plugins/check_curl.d/config.h | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 6d50568f..fc704171 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -139,6 +139,10 @@ int main(int argc, char **argv) { const check_curl_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + check_curl_working_state working_state = config.initial_config; mp_check overall = mp_check_init(); @@ -828,7 +832,8 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { AUTOMATIC_DECOMPRESSION, COOKIE_JAR, HAPROXY_PROTOCOL, - STATE_REGEX + STATE_REGEX, + OUTPUT_FORMAT }; static struct option longopts[] = { @@ -875,6 +880,7 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { {"enable-automatic-decompression", no_argument, 0, AUTOMATIC_DECOMPRESSION}, {"cookie-jar", required_argument, 0, COOKIE_JAR}, {"haproxy-protocol", no_argument, 0, HAPROXY_PROTOCOL}, + {"output-format", required_argument, 0, OUTPUT_FORMAT}, {0, 0, 0, 0}}; check_curl_config_wrapper result = { @@ -1301,6 +1307,18 @@ check_curl_config_wrapper process_arguments(int argc, char **argv) { /* print short usage statement if args not parsable */ usage5(); break; + case OUTPUT_FORMAT: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } } } @@ -1602,6 +1620,8 @@ void print_help(void) { printf(UT_VERBOSE); + printf(UT_OUTPUT_FORMAT); + printf("\n"); printf("%s\n", _("Notes:")); printf(" %s\n", _("This plugin will attempt to open an HTTP connection with the host.")); diff --git a/plugins/check_curl.d/check_curl_helpers.c b/plugins/check_curl.d/check_curl_helpers.c index ab43a1e1..c3c2ba55 100644 --- a/plugins/check_curl.d/check_curl_helpers.c +++ b/plugins/check_curl.d/check_curl_helpers.c @@ -632,6 +632,8 @@ check_curl_config check_curl_config_init() { .show_extended_perfdata = false, .show_body = false, + + .output_format_is_set = false, }; snprintf(tmp.curl_config.user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s, %s)", diff --git a/plugins/check_curl.d/config.h b/plugins/check_curl.d/config.h index 6100af3e..f51b2ee9 100644 --- a/plugins/check_curl.d/config.h +++ b/plugins/check_curl.d/config.h @@ -108,6 +108,9 @@ typedef struct { bool show_extended_perfdata; bool show_body; + + bool output_format_is_set; + mp_output_format output_format; } check_curl_config; check_curl_config check_curl_config_init(); -- cgit v1.2.3-74-g34f1 From 67c1aae749196695802070ec7f0634a096e8c70d Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 10:46:22 +0200 Subject: check_apt: improve some variable types to make the linter happy --- plugins/check_apt.c | 22 +++++++++++----------- plugins/check_apt.d/config.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/check_apt.c b/plugins/check_apt.c index e840184b..15febd4d 100644 --- a/plugins/check_apt.c +++ b/plugins/check_apt.c @@ -29,11 +29,11 @@ * *****************************************************************************/ -#include "states.h" const char *progname = "check_apt"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; +#include "states.h" #include "common.h" #include "runcmd.h" #include "utils.h" @@ -71,8 +71,8 @@ static int run_update(char * /*update_opts*/); typedef struct { int errorcode; - int package_count; - int security_package_count; + size_t package_count; + size_t security_package_count; char **packages_list; char **secpackages_list; } run_upgrade_result; @@ -126,8 +126,8 @@ int main(int argc, char **argv) { run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical, config.upgrade_opts, config.input_filename); result = max_state(result, upgrad_res.errorcode); - int packages_available = upgrad_res.package_count; - int sec_count = upgrad_res.security_package_count; + size_t packages_available = upgrad_res.package_count; + size_t sec_count = upgrad_res.security_package_count; char **packages_list = upgrad_res.packages_list; char **secpackages_list = upgrad_res.secpackages_list; @@ -139,7 +139,7 @@ int main(int argc, char **argv) { result = STATE_UNKNOWN; } - printf(_("APT %s: %d packages available for %s (%d critical updates). %s%s%s%s|available_upgrades=%d;;;0 critical_updates=%d;;;0\n"), + printf(_("APT %s: %zu packages available for %s (%zu critical updates). %s%s%s%s|available_upgrades=%zu;;;0 critical_updates=%zu;;;0\n"), state_text(result), packages_available, (config.upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count); @@ -148,12 +148,12 @@ int main(int argc, char **argv) { qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp); qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp); - for (int i = 0; i < sec_count; i++) { + for (size_t i = 0; i < sec_count; i++) { printf("%s (security)\n", secpackages_list[i]); } if (!config.only_critical) { - for (int i = 0; i < packages_available - sec_count; i++) { + for (size_t i = 0; i < packages_available - sec_count; i++) { printf("%s\n", packages_list[i]); } } @@ -349,8 +349,8 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ * we may need to switch to the --print-uris output format, * in which case the logic here will slightly change. */ - int package_counter = 0; - int security_package_counter = 0; + size_t package_counter = 0; + size_t security_package_counter = 0; for (size_t i = 0; i < chld_out.lines; i++) { if (verbose) { printf("%s\n", chld_out.line[i]); @@ -520,7 +520,7 @@ char *construct_cmdline(upgrade_type upgrade, const char *opts) { break; } - int len = 0; + size_t len = 0; len += strlen(PATH_TO_APTGET) + 1; /* "/usr/bin/apt-get " */ len += strlen(opts_ptr) + 1; /* "opts " */ len += strlen(aptcmd) + 1; /* "upgrade\0" */ diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h index 981f4f42..7355937b 100644 --- a/plugins/check_apt.d/config.h +++ b/plugins/check_apt.d/config.h @@ -16,7 +16,7 @@ typedef struct { bool only_critical; /* whether to warn about non-critical updates */ bool list; /* list packages available for upgrade */ /* number of packages available for upgrade to return WARNING status */ - int packages_warning; + size_t packages_warning; char *upgrade_opts; /* options to override defaults for upgrade */ char *update_opts; /* options to override defaults for update */ -- cgit v1.2.3-74-g34f1 From f07f96c3cfaeb30f95eb1898d6cc58b200e70831 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:40:24 +0200 Subject: check_apt: implement new output --- plugins/check_apt.c | 237 ++++++++++++++++++++++++++++++------------- plugins/check_apt.d/config.h | 7 +- 2 files changed, 172 insertions(+), 72 deletions(-) diff --git a/plugins/check_apt.c b/plugins/check_apt.c index 15febd4d..5aaf1eb6 100644 --- a/plugins/check_apt.c +++ b/plugins/check_apt.c @@ -29,31 +29,33 @@ * *****************************************************************************/ +#include "perfdata.h" const char *progname = "check_apt"; const char *copyright = "2006-2024"; const char *email = "devel@monitoring-plugins.org"; #include "states.h" +#include "output.h" #include "common.h" #include "runcmd.h" #include "utils.h" #include "regex.h" #include "check_apt.d/config.h" -/* Character for hidden input file option (for testing). */ -#define INPUT_FILE_OPT CHAR_MAX + 1 /* the default opts can be overridden via the cmdline */ -#define UPGRADE_DEFAULT_OPTS "-o 'Debug::NoLocking=true' -s -qq" -#define UPDATE_DEFAULT_OPTS "-q" +const char *UPGRADE_DEFAULT_OPTS = "-o 'Debug::NoLocking=true' -s -qq"; +const char *UPDATE_DEFAULT_OPTS = "-q"; + /* until i commit the configure.in patch which gets this, i'll define * it here as well */ #ifndef PATH_TO_APTGET # define PATH_TO_APTGET "/usr/bin/apt-get" #endif /* PATH_TO_APTGET */ + /* String found at the beginning of the apt output lines we're interested in */ -#define PKGINST_PREFIX "Inst " +const char *PKGINST_PREFIX = "Inst "; /* the RE that catches security updates */ -#define SECURITY_RE "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)" +const char *SECURITY_RE = "^[^\\(]*\\(.* (Debian-Security:|Ubuntu:[^/]*/[^-]*-security)"; /* some standard functions */ typedef struct { @@ -66,8 +68,14 @@ void print_usage(void); /* construct the appropriate apt-get cmdline */ static char *construct_cmdline(upgrade_type /*u*/, const char * /*opts*/); + /* run an apt-get update */ -static int run_update(char * /*update_opts*/); +typedef struct { + mp_subcheck sc; + bool stderr_warning; + bool exec_warning; +} run_update_result; +static run_update_result run_update(char *update_opts); typedef struct { int errorcode; @@ -75,11 +83,13 @@ typedef struct { size_t security_package_count; char **packages_list; char **secpackages_list; + bool exec_warning; } run_upgrade_result; /* run an apt-get upgrade */ -run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical, - const char *upgrade_opts, const char *input_filename); +run_upgrade_result run_upgrade(upgrade_type upgrade, const char *do_include, const char *do_exclude, + const char *do_critical, const char *upgrade_opts, + const char *input_filename); /* add another clause to a regexp */ static char *add_to_regexp(char * /*expr*/, const char * /*next*/); @@ -107,6 +117,10 @@ int main(int argc, char **argv) { const check_apt_config config = tmp_config.config; + if (config.output_format_is_set) { + mp_set_format(config.output_format); + } + /* Set signal handling and alarm timeout */ if (signal(SIGALRM, timeout_alarm_handler) == SIG_ERR) { usage_va(_("Cannot catch SIGALRM")); @@ -115,55 +129,91 @@ int main(int argc, char **argv) { /* handle timeouts gracefully... */ alarm(timeout_interval); - mp_state_enum result = STATE_UNKNOWN; + mp_check overall = mp_check_init(); /* if they want to run apt-get update first... */ if (config.do_update) { - result = run_update(config.update_opts); + run_update_result update_result = run_update(config.update_opts); + + mp_add_subcheck_to_check(&overall, update_result.sc); } /* apt-get upgrade */ run_upgrade_result upgrad_res = - run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical, config.upgrade_opts, config.input_filename); + run_upgrade(config.upgrade, config.do_include, config.do_exclude, config.do_critical, + config.upgrade_opts, config.input_filename); + + mp_subcheck sc_run_upgrade = mp_subcheck_init(); + if (upgrad_res.errorcode == OK) { + sc_run_upgrade = mp_set_subcheck_state(sc_run_upgrade, STATE_OK); + } + xasprintf(&sc_run_upgrade.output, "Executed apt upgrade (dry run)"); + + mp_add_subcheck_to_check(&overall, sc_run_upgrade); - result = max_state(result, upgrad_res.errorcode); size_t packages_available = upgrad_res.package_count; - size_t sec_count = upgrad_res.security_package_count; + size_t number_of_security_updates = upgrad_res.security_package_count; char **packages_list = upgrad_res.packages_list; char **secpackages_list = upgrad_res.secpackages_list; - if (sec_count > 0) { - result = max_state(result, STATE_CRITICAL); - } else if (packages_available >= config.packages_warning && !config.only_critical) { - result = max_state(result, STATE_WARNING); - } else if (result > STATE_UNKNOWN) { - result = STATE_UNKNOWN; + mp_perfdata pd_security_updates = perfdata_init(); + pd_security_updates.value = mp_create_pd_value(number_of_security_updates); + pd_security_updates.label = "critical_updates"; + + mp_subcheck sc_security_updates = mp_subcheck_init(); + xasprintf(&sc_security_updates.output, "Security updates available: %zu", + number_of_security_updates); + mp_add_perfdata_to_subcheck(&sc_security_updates, pd_security_updates); + + if (number_of_security_updates > 0) { + sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_CRITICAL); + } else { + sc_security_updates = mp_set_subcheck_state(sc_security_updates, STATE_OK); } - printf(_("APT %s: %zu packages available for %s (%zu critical updates). %s%s%s%s|available_upgrades=%zu;;;0 critical_updates=%zu;;;0\n"), - state_text(result), packages_available, (config.upgrade == DIST_UPGRADE) ? "dist-upgrade" : "upgrade", sec_count, - (stderr_warning) ? " warnings detected" : "", (stderr_warning && exec_warning) ? "," : "", - (exec_warning) ? " errors detected" : "", (stderr_warning || exec_warning) ? "." : "", packages_available, sec_count); + mp_perfdata pd_other_updates = perfdata_init(); + pd_other_updates.value = mp_create_pd_value(packages_available); + pd_other_updates.label = "available_upgrades"; + + mp_subcheck sc_other_updates = mp_subcheck_init(); + + xasprintf(&sc_other_updates.output, "Updates available: %zu", packages_available); + mp_set_subcheck_default_state(sc_other_updates, STATE_OK); + mp_add_perfdata_to_subcheck(&sc_other_updates, pd_other_updates); + + if (packages_available >= config.packages_warning && !config.only_critical) { + sc_other_updates = mp_set_subcheck_state(sc_other_updates, STATE_WARNING); + } if (config.list) { - qsort(secpackages_list, sec_count, sizeof(char *), cmpstringp); - qsort(packages_list, packages_available - sec_count, sizeof(char *), cmpstringp); + qsort(secpackages_list, number_of_security_updates, sizeof(char *), cmpstringp); + qsort(packages_list, packages_available - number_of_security_updates, sizeof(char *), + cmpstringp); - for (size_t i = 0; i < sec_count; i++) { - printf("%s (security)\n", secpackages_list[i]); + for (size_t i = 0; i < number_of_security_updates; i++) { + xasprintf(&sc_security_updates.output, "%s\n%s (security)", sc_security_updates.output, + secpackages_list[i]); } if (!config.only_critical) { - for (size_t i = 0; i < packages_available - sec_count; i++) { - printf("%s\n", packages_list[i]); + for (size_t i = 0; i < packages_available - number_of_security_updates; i++) { + xasprintf(&sc_other_updates.output, "%s\n%s", sc_other_updates.output, + packages_list[i]); } } } + mp_add_subcheck_to_check(&overall, sc_security_updates); + mp_add_subcheck_to_check(&overall, sc_other_updates); - return result; + mp_exit(overall); } /* process command-line arguments */ check_apt_config_wrapper process_arguments(int argc, char **argv) { + enum { + /* Character for hidden input file option (for testing). */ + INPUT_FILE_OPT = CHAR_MAX + 1, + output_format_index, + }; static struct option longopts[] = {{"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, @@ -179,6 +229,7 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) { {"only-critical", no_argument, 0, 'o'}, {"input-file", required_argument, 0, INPUT_FILE_OPT}, {"packages-warning", required_argument, 0, 'w'}, + {"output-format", required_argument, 0, output_format_index}, {0, 0, 0, 0}}; check_apt_config_wrapper result = { @@ -257,6 +308,18 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) { case 'w': result.config.packages_warning = atoi(optarg); break; + case output_format_index: { + parsed_output_format parser = mp_parse_output_format(optarg); + if (!parser.parsing_success) { + // TODO List all available formats here, maybe add anothoer usage function + printf("Invalid output format: %s\n", optarg); + exit(STATE_UNKNOWN); + } + + result.config.output_format_is_set = true; + result.config.output_format = parser.output_format; + break; + } default: /* print short usage statement if args not parsable */ usage5(); @@ -267,37 +330,38 @@ check_apt_config_wrapper process_arguments(int argc, char **argv) { } /* run an apt-get upgrade */ -run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include, const char *do_exclude, const char *do_critical, +run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_include, + const char *do_exclude, const char *do_critical, const char *upgrade_opts, const char *input_filename) { - regex_t ereg; + regex_t exclude_regex; /* initialize ereg as it is possible it is printed while uninitialized */ - memset(&ereg, '\0', sizeof(ereg.buffer)); + memset(&exclude_regex, '\0', sizeof(exclude_regex.buffer)); run_upgrade_result result = { - .errorcode = STATE_UNKNOWN, + .errorcode = OK, }; if (upgrade == NO_UPGRADE) { - result.errorcode = STATE_OK; + result.errorcode = OK; return result; } int regres = 0; - regex_t ireg; + regex_t include_regex; char rerrbuf[64]; /* compile the regexps */ if (do_include != NULL) { - regres = regcomp(&ireg, do_include, REG_EXTENDED); + regres = regcomp(&include_regex, do_include, REG_EXTENDED); if (regres != 0) { - regerror(regres, &ireg, rerrbuf, 64); + regerror(regres, &include_regex, rerrbuf, 64); die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); } } if (do_exclude != NULL) { - regres = regcomp(&ereg, do_exclude, REG_EXTENDED); + regres = regcomp(&exclude_regex, do_exclude, REG_EXTENDED); if (regres != 0) { - regerror(regres, &ereg, rerrbuf, 64); + regerror(regres, &exclude_regex, rerrbuf, 64); die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); } } @@ -306,7 +370,7 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ const char *crit_ptr = (do_critical != NULL) ? do_critical : SECURITY_RE; regres = regcomp(&sreg, crit_ptr, REG_EXTENDED); if (regres != 0) { - regerror(regres, &ereg, rerrbuf, 64); + regerror(regres, &exclude_regex, rerrbuf, 64); die(STATE_UNKNOWN, _("%s: Error compiling regexp: %s"), progname, rerrbuf); } @@ -322,13 +386,12 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ result.errorcode = np_runcmd(cmdline, &chld_out, &chld_err, 0); } - /* apt-get upgrade only changes exit status if there is an - * internal error when run in dry-run mode. therefore we will - * treat such an error as UNKNOWN */ - if (result.errorcode != STATE_OK) { - exec_warning = 1; - result.errorcode = STATE_UNKNOWN; - fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); + // apt-get upgrade only changes exit status if there is an + // internal error when run in dry-run mode. + if (result.errorcode != 0) { + result.exec_warning = true; + result.errorcode = ERROR; + // fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); } char **pkglist = malloc(sizeof(char *) * chld_out.lines); @@ -355,21 +418,25 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ if (verbose) { printf("%s\n", chld_out.line[i]); } + /* if it is a package we care about */ if (strncmp(PKGINST_PREFIX, chld_out.line[i], strlen(PKGINST_PREFIX)) == 0 && - (do_include == NULL || regexec(&ireg, chld_out.line[i], 0, NULL, 0) == 0)) { + (do_include == NULL || regexec(&include_regex, chld_out.line[i], 0, NULL, 0) == 0)) { /* if we're not excluding, or it's not in the * list of stuff to exclude */ - if (do_exclude == NULL || regexec(&ereg, chld_out.line[i], 0, NULL, 0) != 0) { + if (do_exclude == NULL || regexec(&exclude_regex, chld_out.line[i], 0, NULL, 0) != 0) { package_counter++; if (regexec(&sreg, chld_out.line[i], 0, NULL, 0) == 0) { security_package_counter++; + if (verbose) { printf("*"); } + (secpkglist)[security_package_counter - 1] = pkg_name(chld_out.line[i]); } else { - (pkglist)[package_counter - security_package_counter - 1] = pkg_name(chld_out.line[i]); + (pkglist)[package_counter - security_package_counter - 1] = + pkg_name(chld_out.line[i]); } if (verbose) { printf("*%s\n", chld_out.line[i]); @@ -377,6 +444,7 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ } } } + result.package_count = package_counter; result.security_package_count = security_package_counter; result.packages_list = pkglist; @@ -385,41 +453,55 @@ run_upgrade_result run_upgrade(const upgrade_type upgrade, const char *do_includ /* If we get anything on stderr, at least set warning */ if (input_filename == NULL && chld_err.buflen) { stderr_warning = true; - result.errorcode = max_state(result.errorcode, STATE_WARNING); + result.errorcode = ERROR; + if (verbose) { for (size_t i = 0; i < chld_err.lines; i++) { fprintf(stderr, "%s\n", chld_err.line[i]); } } } + if (do_include != NULL) { - regfree(&ireg); + regfree(&include_regex); } + regfree(&sreg); + if (do_exclude != NULL) { - regfree(&ereg); + regfree(&exclude_regex); } + free(cmdline); + return result; } /* run an apt-get update (needs root) */ -int run_update(char *update_opts) { - int result = STATE_UNKNOWN; +run_update_result run_update(char *update_opts) { char *cmdline; /* run the update */ cmdline = construct_cmdline(NO_UPGRADE, update_opts); + run_update_result result = { + .exec_warning = false, + .stderr_warning = false, + .sc = mp_subcheck_init(), + }; + + result.sc = mp_set_subcheck_default_state(result.sc, STATE_OK); + xasprintf(&result.sc.output, "executing '%s' first", cmdline); + struct output chld_out; struct output chld_err; - result = np_runcmd(cmdline, &chld_out, &chld_err, 0); + int cmd_error = np_runcmd(cmdline, &chld_out, &chld_err, 0); /* apt-get update changes exit status if it can't fetch packages. * since we were explicitly asked to do so, this is treated as * a critical error. */ - if (result != 0) { + if (cmd_error != 0) { exec_warning = true; - result = STATE_CRITICAL; - fprintf(stderr, _("'%s' exited with non-zero status.\n"), cmdline); + result.sc = mp_set_subcheck_state(result.sc, STATE_CRITICAL); + xasprintf(&result.sc.output, _("'%s' exited with non-zero status.\n"), cmdline); } if (verbose) { @@ -430,15 +512,18 @@ int run_update(char *update_opts) { /* If we get anything on stderr, at least set warning */ if (chld_err.buflen) { - stderr_warning = 1; - result = max_state(result, STATE_WARNING); + stderr_warning = true; + result.sc = mp_set_subcheck_state( + result.sc, max_state(mp_compute_subcheck_state(result.sc), STATE_WARNING)); if (verbose) { for (size_t i = 0; i < chld_err.lines; i++) { fprintf(stderr, "%s\n", chld_err.line[i]); } } } + free(cmdline); + return result; } @@ -558,7 +643,8 @@ void print_help(void) { printf(" %s\n", _("List packages available for upgrade. Packages are printed sorted by")); printf(" %s\n", _("name with security packages listed first.")); printf(" %s\n", "-i, --include=REGEXP"); - printf(" %s\n", _("Include only packages matching REGEXP. Can be specified multiple times")); + printf(" %s\n", + _("Include only packages matching REGEXP. Can be specified multiple times")); printf(" %s\n", _("the values will be combined together. Any packages matching this list")); printf(" %s\n", _("cause the plugin to return WARNING status. Others will be ignored.")); printf(" %s\n", _("Default is to include all packages.")); @@ -567,7 +653,8 @@ void print_help(void) { printf(" %s\n", _("otherwise be included. Can be specified multiple times; the values")); printf(" %s\n", _("will be combined together. Default is to exclude no packages.")); printf(" %s\n", "-c, --critical=REGEXP"); - printf(" %s\n", _("If the full package information of any of the upgradable packages match")); + printf(" %s\n", + _("If the full package information of any of the upgradable packages match")); printf(" %s\n", _("this REGEXP, the plugin will return CRITICAL status. Can be specified")); printf(" %s\n", _("multiple times like above. Default is a regexp matching security")); printf(" %s\n", _("upgrades for Debian and Ubuntu:")); @@ -576,15 +663,21 @@ void print_help(void) { printf(" %s\n", _("information is compared against the critical list.")); printf(" %s\n", "-o, --only-critical"); printf(" %s\n", _("Only warn about upgrades matching the critical list. The total number")); - printf(" %s\n", _("of upgrades will be printed, but any non-critical upgrades will not cause")); + printf(" %s\n", + _("of upgrades will be printed, but any non-critical upgrades will not cause")); printf(" %s\n", _("the plugin to return WARNING status.")); printf(" %s\n", "-w, --packages-warning"); - printf(" %s\n", _("Minimum number of packages available for upgrade to return WARNING status.")); + printf(" %s\n", + _("Minimum number of packages available for upgrade to return WARNING status.")); printf(" %s\n\n", _("Default is 1 package.")); - printf("%s\n\n", _("The following options require root privileges and should be used with care:")); + printf(UT_OUTPUT_FORMAT); + + printf("%s\n\n", + _("The following options require root privileges and should be used with care:")); printf(" %s\n", "-u, --update=OPTS"); - printf(" %s\n", _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); + printf(" %s\n", + _("First perform an 'apt-get update'. An optional OPTS parameter overrides")); printf(" %s\n", _("the default options. Note: you may also need to adjust the global")); printf(" %s\n", _("timeout (with -t) to prevent the plugin from timing out if apt-get")); printf(" %s\n", _("upgrade is expected to take longer than the default timeout.")); @@ -593,8 +686,10 @@ void print_help(void) { printf(" %s\n", _("apt-get will be run with these command line options instead of the")); printf(" %s", _("default ")); printf("(%s).\n", UPGRADE_DEFAULT_OPTS); - printf(" %s\n", _("Note that you may be required to have root privileges if you do not use")); - printf(" %s\n", _("the default options, which will only run a simulation and NOT perform the upgrade")); + printf(" %s\n", + _("Note that you may be required to have root privileges if you do not use")); + printf(" %s\n", + _("the default options, which will only run a simulation and NOT perform the upgrade")); printf(" %s\n", "-d, --dist-upgrade=OPTS"); printf(" %s\n", _("Perform a dist-upgrade instead of normal upgrade. Like with -U OPTS")); printf(" %s\n", _("can be provided to override the default options.")); diff --git a/plugins/check_apt.d/config.h b/plugins/check_apt.d/config.h index 7355937b..e4d622f1 100644 --- a/plugins/check_apt.d/config.h +++ b/plugins/check_apt.d/config.h @@ -2,6 +2,7 @@ #include "../../config.h" #include +#include "../lib/output.h" /* some constants */ typedef enum { @@ -24,6 +25,9 @@ typedef struct { char *do_exclude; /* regexp to only exclude certain packages */ char *do_critical; /* regexp specifying critical packages */ char *input_filename; /* input filename for testing */ + + bool output_format_is_set; + mp_output_format output_format; } check_apt_config; check_apt_config check_apt_config_init() { @@ -36,6 +40,7 @@ check_apt_config check_apt_config_init() { .do_include = NULL, .do_exclude = NULL, .do_critical = NULL, - .input_filename = NULL}; + .input_filename = NULL, + .output_format_is_set = false}; return tmp; } -- cgit v1.2.3-74-g34f1 From 802e46f8ea36c344f112d7e1dd8d64d17a4cc939 Mon Sep 17 00:00:00 2001 From: Lorenz Kästle <12514511+RincewindsHat@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:59:37 +0200 Subject: Run clang-format again --- lib/extra_opts.c | 28 +- lib/maxfd.c | 5 +- lib/output.c | 11 +- lib/parse_ini.c | 112 +++-- lib/tests/test_base64.c | 271 +++++++----- lib/tests/test_cmd.c | 27 +- lib/tests/test_generic_output.c | 6 +- lib/tests/test_ini1.c | 51 ++- lib/tests/test_opts1.c | 34 +- lib/tests/test_opts2.c | 69 +-- lib/tests/test_tcp.c | 23 +- lib/utils_base.c | 4 +- lib/utils_cmd.c | 95 ++-- lib/utils_tcp.c | 25 +- lib/utils_tcp.h | 3 +- plugins-root/check_dhcp.c | 258 +++++++---- plugins-root/check_icmp.d/config.h | 2 +- plugins-root/pst3.c | 441 +++++++++---------- plugins/check_by_ssh.c | 108 +++-- plugins/check_cluster.c | 26 +- plugins/check_dbi.c | 108 +++-- plugins/check_dig.c | 18 +- plugins/check_disk.c | 288 +++++++----- plugins/check_disk.d/utils_disk.c | 35 +- plugins/check_disk.d/utils_disk.h | 9 +- plugins/check_dns.c | 133 ++++-- plugins/check_dummy.c | 15 +- plugins/check_fping.c | 58 ++- plugins/check_fping.d/config.h | 1 - plugins/check_game.c | 68 +-- plugins/check_hpjd.c | 14 +- plugins/check_ldap.c | 50 ++- plugins/check_load.c | 24 +- plugins/check_mrtg.c | 53 ++- plugins/check_mrtgtraf.c | 32 +- plugins/check_mysql.c | 88 ++-- plugins/check_mysql_query.c | 37 +- plugins/check_nt.c | 115 +++-- plugins/check_ntp.c | 756 +++++++++++++++++--------------- plugins/check_ntp_peer.c | 118 ++--- plugins/check_ntp_time.c | 91 ++-- plugins/check_pgsql.c | 77 ++-- plugins/check_ping.c | 125 ++++-- plugins/check_procs.c | 93 ++-- plugins/check_radius.c | 36 +- plugins/check_real.c | 28 +- plugins/check_smtp.c | 119 +++-- plugins/check_swap.c | 23 +- plugins/check_swap.d/check_swap.h | 3 +- plugins/check_swap.d/swap.c | 18 +- plugins/check_tcp.c | 147 ++++--- plugins/check_time.c | 25 +- plugins/check_ups.c | 47 +- plugins/check_ups.d/config.h | 1 - plugins/check_users.c | 19 +- plugins/check_users.d/users.c | 6 +- plugins/common.h | 175 ++++---- plugins/negate.c | 44 +- plugins/netutils.c | 12 +- plugins/picohttpparser/picohttpparser.c | 243 +++++----- plugins/picohttpparser/picohttpparser.h | 31 +- plugins/popen.c | 68 ++- plugins/popen.h | 20 +- plugins/runcmd.c | 77 ++-- plugins/runcmd.h | 47 +- plugins/sslutils.c | 6 +- plugins/tests/test_check_disk.c | 48 +- plugins/tests/test_check_swap.c | 4 +- plugins/urlize.c | 36 +- plugins/utils.c | 35 +- plugins/utils.h | 51 ++- tap/tap.c | 25 +- tap/tap.h | 43 +- tools/mini_epn.c | 96 ++-- 74 files changed, 3290 insertions(+), 2248 deletions(-) diff --git a/lib/extra_opts.c b/lib/extra_opts.c index 88787336..857b34b4 100644 --- a/lib/extra_opts.c +++ b/lib/extra_opts.c @@ -27,12 +27,13 @@ /* FIXME: copied from utils.h; we should move a bunch of libs! */ bool is_option2(char *str) { - if (!str) + if (!str) { return false; - else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) + } else if (strspn(str, "-") == 1 || strspn(str, "-") == 2) { return true; - else + } else { return false; + } } /* this is the externally visible function used by plugins */ @@ -56,8 +57,9 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* It is a single argument with value */ argptr = argv[i] + 13; /* Delete the extra opts argument */ - for (j = i; j < *argc; j++) + for (j = i; j < *argc; j++) { argv[j] = argv[j + 1]; + } i--; *argc -= 1; } else if (strcmp(argv[i], "--extra-opts") == 0) { @@ -65,8 +67,9 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* It is a argument with separate value */ argptr = argv[i + 1]; /* Delete the extra-opts argument/value */ - for (j = i; j < *argc - 1; j++) + for (j = i; j < *argc - 1; j++) { argv[j] = argv[j + 2]; + } i -= 2; *argc -= 2; ea_num--; @@ -74,8 +77,9 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* It has no value */ optfound = 1; /* Delete the extra opts argument */ - for (j = i; j < *argc; j++) + for (j = i; j < *argc; j++) { argv[j] = argv[j + 1]; + } i--; *argc -= 1; } @@ -94,16 +98,18 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* append the list to extra_args */ if (extra_args == NULL) { extra_args = ea1; - while ((ea1 = ea1->next)) + while ((ea1 = ea1->next)) { ea_num++; + } } else { ea_tmp = extra_args; while (ea_tmp->next) { ea_tmp = ea_tmp->next; } ea_tmp->next = ea1; - while ((ea1 = ea1->next)) + while ((ea1 = ea1->next)) { ea_num++; + } } ea1 = ea_tmp = NULL; } @@ -116,8 +122,9 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { /* done processing arguments. now create a new argv array... */ argv_new = (char **)malloc((ea_num + 1) * sizeof(char **)); - if (argv_new == NULL) + if (argv_new == NULL) { die(STATE_UNKNOWN, _("malloc() failed!\n")); + } /* starting with program name */ argv_new[0] = argv[0]; @@ -130,8 +137,9 @@ char **np_extra_opts(int *argc, char **argv, const char *plugin_name) { free(ea1); } /* finally the rest of the argv array */ - for (i = 1; i < *argc; i++) + for (i = 1; i < *argc; i++) { argv_new[argc_new++] = argv[i]; + } *argc = argc_new; /* and terminate. */ argv_new[argc_new] = NULL; diff --git a/lib/maxfd.c b/lib/maxfd.c index ca5b6e54..9b58d8e3 100644 --- a/lib/maxfd.c +++ b/lib/maxfd.c @@ -31,10 +31,11 @@ long mp_open_max(void) { #ifdef _SC_OPEN_MAX errno = 0; if ((maxfd = sysconf(_SC_OPEN_MAX)) < 0) { - if (errno == 0) + if (errno == 0) { maxfd = DEFAULT_MAXFD; /* it's indeterminate */ - else + } else { die(STATE_UNKNOWN, _("sysconf error for _SC_OPEN_MAX\n")); + } } #elif defined(OPEN_MAX) return OPEN_MAX diff --git a/lib/output.c b/lib/output.c index 34ff7dab..f283969f 100644 --- a/lib/output.c +++ b/lib/output.c @@ -377,7 +377,9 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch while (tmp_string != NULL) { *tmp_string = '\0'; - asprintf(&intermediate_string, "%s%s\n%s", intermediate_string,check.output, generate_indentation_string(indentation+1)); // one more indentation to make it look better + asprintf(&intermediate_string, "%s%s\n%s", intermediate_string, check.output, + generate_indentation_string( + indentation + 1)); // one more indentation to make it look better if (*(tmp_string + 1) != '\0') { check.output = tmp_string + 1; @@ -394,13 +396,14 @@ static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subch // add the rest (if any) if (have_residual_chars) { char *tmp = check.output; - xasprintf(&check.output, "%s\n%s%s", intermediate_string, generate_indentation_string(indentation+1), tmp); + xasprintf(&check.output, "%s\n%s%s", intermediate_string, + generate_indentation_string(indentation + 1), tmp); } else { check.output = intermediate_string; } } - asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), - check.output); + asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), + state_text(mp_compute_subcheck_state(check)), check.output); subchecks = check.subchecks; diff --git a/lib/parse_ini.c b/lib/parse_ini.c index 1289aae2..4c3c1b93 100644 --- a/lib/parse_ini.c +++ b/lib/parse_ini.c @@ -40,19 +40,22 @@ typedef struct { char *stanza; } np_ini_info; -static char *default_ini_file_names[] = {"monitoring-plugins.ini", "plugins.ini", "nagios-plugins.ini", NULL}; +static char *default_ini_file_names[] = {"monitoring-plugins.ini", "plugins.ini", + "nagios-plugins.ini", NULL}; static char *default_ini_path_names[] = { - "/usr/local/etc/monitoring-plugins/monitoring-plugins.ini", "/usr/local/etc/monitoring-plugins.ini", - "/etc/monitoring-plugins/monitoring-plugins.ini", "/etc/monitoring-plugins.ini", + "/usr/local/etc/monitoring-plugins/monitoring-plugins.ini", + "/usr/local/etc/monitoring-plugins.ini", "/etc/monitoring-plugins/monitoring-plugins.ini", + "/etc/monitoring-plugins.ini", /* deprecated path names (for backward compatibility): */ - "/etc/nagios/plugins.ini", "/usr/local/nagios/etc/plugins.ini", "/usr/local/etc/nagios/plugins.ini", "/etc/opt/nagios/plugins.ini", - "/etc/nagios-plugins.ini", "/usr/local/etc/nagios-plugins.ini", "/etc/opt/nagios-plugins.ini", NULL}; + "/etc/nagios/plugins.ini", "/usr/local/nagios/etc/plugins.ini", + "/usr/local/etc/nagios/plugins.ini", "/etc/opt/nagios/plugins.ini", "/etc/nagios-plugins.ini", + "/usr/local/etc/nagios-plugins.ini", "/etc/opt/nagios-plugins.ini", NULL}; /* eat all characters from a FILE pointer until n is encountered */ -#define GOBBLE_TO(f, c, n) \ - do { \ - (c) = fgetc((f)); \ +#define GOBBLE_TO(f, c, n) \ + do { \ + (c) = fgetc((f)); \ } while ((c) != EOF && (c) != (n)) /* internal function that returns the constructed defaults options */ @@ -87,8 +90,9 @@ static void parse_locator(const char *locator, const char *def_stanza, np_ini_in i->stanza = strdup(def_stanza); } - if (i->stanza == NULL) + if (i->stanza == NULL) { die(STATE_UNKNOWN, _("malloc() failed!\n")); + } /* check whether there's an @file part */ if (stanza_len == locator_len) { @@ -99,8 +103,9 @@ static void parse_locator(const char *locator, const char *def_stanza, np_ini_in i->file_string_on_heap = true; } - if (i->file == NULL || i->file[0] == '\0') + if (i->file == NULL || i->file[0] == '\0') { die(STATE_UNKNOWN, _("Cannot find config file in any standard location.\n")); + } } /* @@ -112,26 +117,31 @@ np_arg_list *np_get_defaults(const char *locator, const char *default_section) { np_ini_info i; int is_suid_plugin = mp_suid(); - if (is_suid_plugin && idpriv_temp_drop() == -1) + if (is_suid_plugin && idpriv_temp_drop() == -1) { die(STATE_UNKNOWN, _("Cannot drop privileges: %s\n"), strerror(errno)); + } parse_locator(locator, default_section, &i); inifile = strcmp(i.file, "-") == 0 ? stdin : fopen(i.file, "r"); - if (inifile == NULL) + if (inifile == NULL) { die(STATE_UNKNOWN, _("Can't read config file: %s\n"), strerror(errno)); - if (!read_defaults(inifile, i.stanza, &defaults)) + } + if (!read_defaults(inifile, i.stanza, &defaults)) { die(STATE_UNKNOWN, _("Invalid section '%s' in config file '%s'\n"), i.stanza, i.file); + } if (i.file_string_on_heap) { free(i.file); } - if (inifile != stdin) + if (inifile != stdin) { fclose(inifile); + } free(i.stanza); - if (is_suid_plugin && idpriv_temp_restore() == -1) + if (is_suid_plugin && idpriv_temp_restore() == -1) { die(STATE_UNKNOWN, _("Cannot restore privileges: %s\n"), strerror(errno)); + } return defaults; } @@ -158,8 +168,9 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { /* our little stanza-parsing state machine */ while ((c = fgetc(f)) != EOF) { /* gobble up leading whitespace */ - if (isspace(c)) + if (isspace(c)) { continue; + } switch (c) { /* globble up comment lines */ case ';': @@ -172,9 +183,11 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { for (i = 0; i < stanza_len; i++) { c = fgetc(f); /* strip leading whitespace */ - if (i == 0) - for (; isspace(c); c = fgetc(f)) + if (i == 0) { + for (; isspace(c); c = fgetc(f)) { continue; + } + } /* nope, read to the end of the line */ if (c != stanza[i]) { GOBBLE_TO(f, c, '\n'); @@ -185,10 +198,12 @@ static int read_defaults(FILE *f, const char *stanza, np_arg_list **opts) { if (i == stanza_len) { c = fgetc(f); /* strip trailing whitespace */ - for (; isspace(c); c = fgetc(f)) + for (; isspace(c); c = fgetc(f)) { continue; - if (c == ']') + } + if (c == ']') { stanzastate = RIGHTSTANZA; + } } break; /* otherwise, we're in the body of a stanza or a parse error */ @@ -239,12 +254,13 @@ static int add_option(FILE *f, np_arg_list **optlst) { if (linebuf == NULL || read_pos + read_sz >= linebuf_sz) { linebuf_sz = linebuf_sz > 0 ? linebuf_sz << 1 : read_sz; linebuf = realloc(linebuf, linebuf_sz); - if (linebuf == NULL) + if (linebuf == NULL) { die(STATE_UNKNOWN, _("malloc() failed!\n")); + } } - if (fgets(&linebuf[read_pos], (int)read_sz, f) == NULL) + if (fgets(&linebuf[read_pos], (int)read_sz, f) == NULL) { done_reading = 1; - else { + } else { read_pos = strlen(linebuf); if (linebuf[read_pos - 1] == '\n') { linebuf[--read_pos] = '\0'; @@ -256,38 +272,46 @@ static int add_option(FILE *f, np_arg_list **optlst) { /* all that to read one line, isn't C fun? :) now comes the parsing :/ */ /* skip leading whitespace */ - for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++) + for (optptr = linebuf; optptr < lineend && isspace(*optptr); optptr++) { continue; + } /* continue to '=' or EOL, watching for spaces that might precede it */ for (eqptr = optptr; eqptr < lineend && *eqptr != '='; eqptr++) { - if (isspace(*eqptr) && optend == NULL) + if (isspace(*eqptr) && optend == NULL) { optend = eqptr; - else + } else { optend = NULL; + } } - if (optend == NULL) + if (optend == NULL) { optend = eqptr; + } --optend; /* ^[[:space:]]*=foo is a syntax error */ - if (optptr == eqptr) + if (optptr == eqptr) { die(STATE_UNKNOWN, "%s\n", _("Config file error")); + } /* continue from '=' to start of value or EOL */ - for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr); valptr++) + for (valptr = eqptr + 1; valptr < lineend && isspace(*valptr); valptr++) { continue; + } /* continue to the end of value */ - for (valend = valptr; valend < lineend; valend++) + for (valend = valptr; valend < lineend; valend++) { continue; + } --valend; /* finally trim off trailing spaces */ - for (; isspace(*valend); valend--) + for (; isspace(*valend); valend--) { continue; + } /* calculate the length of "--foo" */ opt_len = (size_t)(1 + optend - optptr); /* 1-character params needs only one dash */ - if (opt_len == 1) + if (opt_len == 1) { cfg_len = 1 + (opt_len); - else + } else { cfg_len = 2 + (opt_len); + } /* if valptrarg[read_pos] = '\0'; /* ...and put that to the end of the list */ - if (*optlst == NULL) + if (*optlst == NULL) { *optlst = optnew; - else { - while (opttmp->next != NULL) + } else { + while (opttmp->next != NULL) { opttmp = opttmp->next; + } opttmp->next = optnew; } @@ -344,7 +370,8 @@ static int add_option(FILE *f, np_arg_list **optlst) { static char *default_file(void) { char *ini_file; - if ((ini_file = getenv("MP_CONFIG_FILE")) != NULL || (ini_file = default_file_in_path()) != NULL) { + if ((ini_file = getenv("MP_CONFIG_FILE")) != NULL || + (ini_file = default_file_in_path()) != NULL) { return ini_file; } @@ -360,16 +387,19 @@ static char *default_file_in_path(void) { char *config_path, **file; char *dir, *ini_file, *tokens; - if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL) + if ((config_path = getenv("NAGIOS_CONFIG_PATH")) == NULL) { return NULL; + } /* shall we spit out a warning that NAGIOS_CONFIG_PATH is deprecated? */ - if ((tokens = strdup(config_path)) == NULL) + if ((tokens = strdup(config_path)) == NULL) { die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); + } for (dir = strtok(tokens, ":"); dir != NULL; dir = strtok(NULL, ":")) { for (file = default_ini_file_names; *file != NULL; file++) { - if ((asprintf(&ini_file, "%s/%s", dir, *file)) < 0) + if ((asprintf(&ini_file, "%s/%s", dir, *file)) < 0) { die(STATE_UNKNOWN, "%s\n", _("Insufficient Memory")); + } if (access(ini_file, F_OK) == 0) { free(tokens); return ini_file; diff --git a/lib/tests/test_base64.c b/lib/tests/test_base64.c index 94cb5aa9..798244da 100644 --- a/lib/tests/test_base64.c +++ b/lib/tests/test_base64.c @@ -180,117 +180,168 @@ int main(int argc, char **argv) { #endif char random[1024] = { - 0x0b, 0x30, 0x44, 0x62, 0x7c, 0x22, 0x1f, 0x0d, 0x05, 0x67, 0x2c, 0x2a, 0x39, 0x21, 0x46, 0x08, 0x50, 0x66, 0x34, 0x37, 0x0b, 0x45, - 0x4b, 0x38, 0x32, 0x06, 0x7a, 0x3e, 0x7f, 0x0c, 0x40, 0x18, 0x6b, 0x2d, 0x60, 0x4c, 0x60, 0x0c, 0x23, 0x43, 0x3b, 0x3e, 0x1b, 0x16, - 0x04, 0x46, 0x58, 0x3f, 0x40, 0x6a, 0x11, 0x05, 0x63, 0x71, 0x14, 0x35, 0x47, 0x79, 0x13, 0x6f, 0x6b, 0x27, 0x18, 0x5b, 0x48, 0x27, - 0x3e, 0x6f, 0x15, 0x33, 0x4f, 0x3e, 0x5e, 0x51, 0x73, 0x68, 0x25, 0x0f, 0x06, 0x5b, 0x7c, 0x72, 0x75, 0x3e, 0x3f, 0x1b, 0x5c, 0x6d, - 0x6a, 0x39, 0x7c, 0x63, 0x63, 0x60, 0x6c, 0x7a, 0x33, 0x76, 0x52, 0x13, 0x25, 0x33, 0x7d, 0x65, 0x23, 0x27, 0x11, 0x06, 0x06, 0x47, - 0x71, 0x1e, 0x14, 0x74, 0x63, 0x70, 0x2d, 0x15, 0x27, 0x18, 0x51, 0x06, 0x05, 0x33, 0x11, 0x2c, 0x6b, 0x00, 0x2d, 0x77, 0x20, 0x48, - 0x0d, 0x73, 0x51, 0x45, 0x25, 0x7f, 0x7f, 0x35, 0x26, 0x2e, 0x26, 0x53, 0x24, 0x68, 0x1e, 0x0e, 0x58, 0x3a, 0x59, 0x50, 0x56, 0x37, - 0x5f, 0x66, 0x01, 0x4c, 0x5a, 0x64, 0x32, 0x50, 0x7b, 0x6a, 0x20, 0x72, 0x2b, 0x1d, 0x7e, 0x43, 0x7b, 0x61, 0x42, 0x0b, 0x61, 0x73, - 0x24, 0x79, 0x3a, 0x6b, 0x4a, 0x79, 0x6e, 0x09, 0x0f, 0x27, 0x2d, 0x0c, 0x5e, 0x32, 0x4b, 0x0d, 0x79, 0x46, 0x39, 0x21, 0x0a, 0x26, - 0x5f, 0x3a, 0x00, 0x26, 0x3f, 0x13, 0x2e, 0x7e, 0x50, 0x2b, 0x67, 0x46, 0x72, 0x3f, 0x3b, 0x01, 0x46, 0x1b, 0x0b, 0x35, 0x49, 0x39, - 0x19, 0x70, 0x3d, 0x02, 0x41, 0x0e, 0x38, 0x05, 0x76, 0x65, 0x4f, 0x31, 0x6c, 0x5e, 0x17, 0x04, 0x15, 0x36, 0x26, 0x64, 0x34, 0x14, - 0x17, 0x7c, 0x0e, 0x0b, 0x5b, 0x55, 0x53, 0x6b, 0x00, 0x42, 0x41, 0x4f, 0x02, 0x5c, 0x13, 0x0a, 0x2c, 0x2c, 0x3e, 0x10, 0x14, 0x33, - 0x45, 0x7c, 0x7a, 0x5a, 0x31, 0x61, 0x39, 0x08, 0x22, 0x6a, 0x1e, 0x0f, 0x6f, 0x1b, 0x6c, 0x13, 0x5e, 0x79, 0x20, 0x79, 0x50, 0x62, - 0x06, 0x2c, 0x76, 0x17, 0x04, 0x2b, 0x2a, 0x75, 0x1f, 0x0c, 0x37, 0x4e, 0x0f, 0x7b, 0x2d, 0x34, 0x75, 0x60, 0x31, 0x74, 0x2e, 0x0a, - 0x4a, 0x11, 0x6c, 0x49, 0x25, 0x01, 0x3a, 0x3d, 0x22, 0x1e, 0x6d, 0x18, 0x51, 0x78, 0x2d, 0x62, 0x31, 0x4c, 0x50, 0x40, 0x17, 0x4b, - 0x6f, 0x22, 0x00, 0x7f, 0x61, 0x2a, 0x34, 0x3e, 0x00, 0x5f, 0x2f, 0x5f, 0x2f, 0x14, 0x2a, 0x55, 0x27, 0x1f, 0x46, 0x1f, 0x12, 0x46, - 0x5e, 0x1e, 0x0c, 0x7c, 0x38, 0x01, 0x61, 0x64, 0x76, 0x22, 0x6e, 0x08, 0x20, 0x38, 0x4f, 0x73, 0x72, 0x55, 0x12, 0x42, 0x19, 0x50, - 0x61, 0x43, 0x77, 0x7d, 0x41, 0x2e, 0x35, 0x4f, 0x3d, 0x31, 0x28, 0x58, 0x67, 0x1b, 0x03, 0x51, 0x20, 0x32, 0x1c, 0x08, 0x6e, 0x37, - 0x75, 0x37, 0x44, 0x4f, 0x68, 0x19, 0x07, 0x64, 0x14, 0x28, 0x25, 0x2b, 0x69, 0x35, 0x18, 0x27, 0x26, 0x14, 0x13, 0x70, 0x42, 0x19, - 0x12, 0x75, 0x3e, 0x02, 0x5d, 0x7c, 0x13, 0x1f, 0x16, 0x53, 0x3b, 0x74, 0x48, 0x3c, 0x5e, 0x39, 0x6c, 0x1c, 0x1c, 0x74, 0x39, 0x1f, - 0x00, 0x1b, 0x06, 0x0a, 0x68, 0x3b, 0x52, 0x4f, 0x1e, 0x6e, 0x3c, 0x35, 0x0c, 0x38, 0x0e, 0x0b, 0x3b, 0x1a, 0x76, 0x23, 0x29, 0x53, - 0x1e, 0x5f, 0x41, 0x0c, 0x4b, 0x0a, 0x65, 0x28, 0x78, 0x67, 0x48, 0x59, 0x26, 0x6d, 0x31, 0x76, 0x23, 0x70, 0x61, 0x64, 0x3b, 0x38, - 0x79, 0x66, 0x74, 0x53, 0x2c, 0x64, 0x64, 0x54, 0x03, 0x54, 0x65, 0x44, 0x4c, 0x18, 0x4f, 0x48, 0x20, 0x4f, 0x72, 0x10, 0x3f, 0x0c, - 0x52, 0x2d, 0x03, 0x14, 0x03, 0x51, 0x42, 0x10, 0x77, 0x6a, 0x34, 0x06, 0x32, 0x03, 0x72, 0x14, 0x7c, 0x08, 0x5d, 0x52, 0x1a, 0x62, - 0x7c, 0x3e, 0x30, 0x7e, 0x5f, 0x7f, 0x54, 0x0f, 0x44, 0x49, 0x5d, 0x5e, 0x10, 0x6a, 0x06, 0x2b, 0x06, 0x53, 0x10, 0x39, 0x37, 0x32, - 0x4a, 0x4e, 0x3d, 0x2b, 0x65, 0x38, 0x39, 0x07, 0x72, 0x54, 0x64, 0x4d, 0x56, 0x6a, 0x03, 0x22, 0x70, 0x7b, 0x5f, 0x60, 0x0b, 0x2a, - 0x0b, 0x6b, 0x10, 0x64, 0x14, 0x05, 0x22, 0x00, 0x73, 0x40, 0x23, 0x5b, 0x51, 0x1f, 0x2b, 0x1a, 0x5d, 0x69, 0x7a, 0x46, 0x0c, 0x5f, - 0x32, 0x4b, 0x4a, 0x28, 0x52, 0x79, 0x5b, 0x12, 0x42, 0x18, 0x00, 0x5d, 0x27, 0x31, 0x53, 0x3c, 0x4c, 0x36, 0x4e, 0x38, 0x3f, 0x72, - 0x03, 0x71, 0x02, 0x5b, 0x36, 0x59, 0x7f, 0x75, 0x6e, 0x08, 0x54, 0x0d, 0x34, 0x1c, 0x34, 0x57, 0x5d, 0x69, 0x48, 0x00, 0x3b, 0x05, - 0x07, 0x6e, 0x27, 0x65, 0x6e, 0x40, 0x3d, 0x3a, 0x4f, 0x72, 0x5d, 0x39, 0x16, 0x0f, 0x63, 0x12, 0x12, 0x15, 0x3a, 0x70, 0x0d, 0x57, - 0x18, 0x0d, 0x5e, 0x3d, 0x22, 0x68, 0x68, 0x7c, 0x6d, 0x4f, 0x0c, 0x7b, 0x09, 0x2d, 0x4a, 0x73, 0x20, 0x47, 0x07, 0x57, 0x75, 0x5d, - 0x53, 0x70, 0x34, 0x21, 0x40, 0x57, 0x51, 0x5e, 0x49, 0x44, 0x00, 0x54, 0x27, 0x04, 0x68, 0x7e, 0x59, 0x56, 0x58, 0x74, 0x14, 0x3c, - 0x16, 0x33, 0x41, 0x16, 0x4b, 0x2f, 0x49, 0x37, 0x0a, 0x54, 0x08, 0x08, 0x1f, 0x39, 0x67, 0x76, 0x28, 0x28, 0x07, 0x1d, 0x61, 0x47, - 0x51, 0x4d, 0x75, 0x26, 0x52, 0x47, 0x47, 0x0c, 0x57, 0x58, 0x74, 0x3e, 0x62, 0x6c, 0x58, 0x3a, 0x44, 0x1e, 0x16, 0x2e, 0x21, 0x1c, - 0x73, 0x45, 0x67, 0x74, 0x4f, 0x33, 0x66, 0x0e, 0x74, 0x66, 0x26, 0x1f, 0x2e, 0x38, 0x44, 0x40, 0x7e, 0x2a, 0x50, 0x52, 0x5e, 0x43, - 0x01, 0x7a, 0x38, 0x49, 0x3c, 0x55, 0x4d, 0x5a, 0x44, 0x08, 0x26, 0x59, 0x4d, 0x45, 0x0b, 0x48, 0x0a, 0x33, 0x5e, 0x4a, 0x4d, 0x75, - 0x16, 0x17, 0x63, 0x46, 0x01, 0x2a, 0x55, 0x7b, 0x0f, 0x02, 0x73, 0x6a, 0x4b, 0x7f, 0x75, 0x65, 0x3c, 0x4c, 0x33, 0x39, 0x6c, 0x74, - 0x05, 0x60, 0x0f, 0x7f, 0x2d, 0x41, 0x4d, 0x4d, 0x46, 0x71, 0x09, 0x6f, 0x4f, 0x60, 0x15, 0x0f, 0x46, 0x73, 0x63, 0x4c, 0x5e, 0x74, - 0x30, 0x0d, 0x28, 0x43, 0x08, 0x72, 0x32, 0x04, 0x2e, 0x31, 0x29, 0x27, 0x44, 0x6d, 0x13, 0x17, 0x48, 0x0f, 0x49, 0x52, 0x10, 0x13, - 0x7f, 0x17, 0x16, 0x62, 0x79, 0x35, 0x78, 0x3e, 0x01, 0x7c, 0x2e, 0x0f, 0x76, 0x3e, 0x5e, 0x53, 0x6c, 0x5b, 0x5f, 0x7c, 0x19, 0x41, - 0x02, 0x2f, 0x17, 0x64, 0x41, 0x75, 0x10, 0x04, 0x47, 0x7c, 0x3d, 0x4b, 0x52, 0x00, 0x10, 0x5d, 0x51, 0x4e, 0x7a, 0x27, 0x25, 0x55, - 0x40, 0x12, 0x35, 0x60, 0x05, 0x1b, 0x34, 0x2d, 0x04, 0x7a, 0x6a, 0x69, 0x02, 0x79, 0x03, 0x3a, 0x2f, 0x06, 0x0a, 0x79, 0x7b, 0x12, - 0x5d, 0x7c, 0x52, 0x29, 0x47, 0x58, 0x12, 0x73, 0x3f, 0x27, 0x56, 0x05, 0x0c, 0x48, 0x32, 0x58, 0x6b, 0x57, 0x5c, 0x03, 0x64, 0x56, - 0x11, 0x52, 0x7a, 0x30, 0x36, 0x29, 0x17, 0x3b, 0x68, 0x7a, 0x7c, 0x05, 0x6b, 0x6b, 0x13, 0x6a, 0x24, 0x5c, 0x68, 0x42, 0x18, 0x32, - 0x03, 0x73, 0x6e, 0x04, 0x21, 0x2e, 0x01, 0x04, 0x63, 0x7d, 0x44, 0x41, 0x12, 0x31, 0x0b, 0x15, 0x1f, 0x70, 0x00, 0x2e, 0x66, 0x14, - 0x3c, 0x7f, 0x2b, 0x00, 0x1f, 0x0c, 0x28, 0x59, 0x0a, 0x16, 0x49, 0x5a, 0x5c, 0x64, 0x65, 0x4b, 0x11, 0x29, 0x15, 0x36, 0x5a, 0x65, - 0x19, 0x4f, 0x60, 0x23, 0x3a, 0x3a, 0x13, 0x25, 0x02, 0x78, 0x4c, 0x54}; + 0x0b, 0x30, 0x44, 0x62, 0x7c, 0x22, 0x1f, 0x0d, 0x05, 0x67, 0x2c, 0x2a, 0x39, 0x21, 0x46, + 0x08, 0x50, 0x66, 0x34, 0x37, 0x0b, 0x45, 0x4b, 0x38, 0x32, 0x06, 0x7a, 0x3e, 0x7f, 0x0c, + 0x40, 0x18, 0x6b, 0x2d, 0x60, 0x4c, 0x60, 0x0c, 0x23, 0x43, 0x3b, 0x3e, 0x1b, 0x16, 0x04, + 0x46, 0x58, 0x3f, 0x40, 0x6a, 0x11, 0x05, 0x63, 0x71, 0x14, 0x35, 0x47, 0x79, 0x13, 0x6f, + 0x6b, 0x27, 0x18, 0x5b, 0x48, 0x27, 0x3e, 0x6f, 0x15, 0x33, 0x4f, 0x3e, 0x5e, 0x51, 0x73, + 0x68, 0x25, 0x0f, 0x06, 0x5b, 0x7c, 0x72, 0x75, 0x3e, 0x3f, 0x1b, 0x5c, 0x6d, 0x6a, 0x39, + 0x7c, 0x63, 0x63, 0x60, 0x6c, 0x7a, 0x33, 0x76, 0x52, 0x13, 0x25, 0x33, 0x7d, 0x65, 0x23, + 0x27, 0x11, 0x06, 0x06, 0x47, 0x71, 0x1e, 0x14, 0x74, 0x63, 0x70, 0x2d, 0x15, 0x27, 0x18, + 0x51, 0x06, 0x05, 0x33, 0x11, 0x2c, 0x6b, 0x00, 0x2d, 0x77, 0x20, 0x48, 0x0d, 0x73, 0x51, + 0x45, 0x25, 0x7f, 0x7f, 0x35, 0x26, 0x2e, 0x26, 0x53, 0x24, 0x68, 0x1e, 0x0e, 0x58, 0x3a, + 0x59, 0x50, 0x56, 0x37, 0x5f, 0x66, 0x01, 0x4c, 0x5a, 0x64, 0x32, 0x50, 0x7b, 0x6a, 0x20, + 0x72, 0x2b, 0x1d, 0x7e, 0x43, 0x7b, 0x61, 0x42, 0x0b, 0x61, 0x73, 0x24, 0x79, 0x3a, 0x6b, + 0x4a, 0x79, 0x6e, 0x09, 0x0f, 0x27, 0x2d, 0x0c, 0x5e, 0x32, 0x4b, 0x0d, 0x79, 0x46, 0x39, + 0x21, 0x0a, 0x26, 0x5f, 0x3a, 0x00, 0x26, 0x3f, 0x13, 0x2e, 0x7e, 0x50, 0x2b, 0x67, 0x46, + 0x72, 0x3f, 0x3b, 0x01, 0x46, 0x1b, 0x0b, 0x35, 0x49, 0x39, 0x19, 0x70, 0x3d, 0x02, 0x41, + 0x0e, 0x38, 0x05, 0x76, 0x65, 0x4f, 0x31, 0x6c, 0x5e, 0x17, 0x04, 0x15, 0x36, 0x26, 0x64, + 0x34, 0x14, 0x17, 0x7c, 0x0e, 0x0b, 0x5b, 0x55, 0x53, 0x6b, 0x00, 0x42, 0x41, 0x4f, 0x02, + 0x5c, 0x13, 0x0a, 0x2c, 0x2c, 0x3e, 0x10, 0x14, 0x33, 0x45, 0x7c, 0x7a, 0x5a, 0x31, 0x61, + 0x39, 0x08, 0x22, 0x6a, 0x1e, 0x0f, 0x6f, 0x1b, 0x6c, 0x13, 0x5e, 0x79, 0x20, 0x79, 0x50, + 0x62, 0x06, 0x2c, 0x76, 0x17, 0x04, 0x2b, 0x2a, 0x75, 0x1f, 0x0c, 0x37, 0x4e, 0x0f, 0x7b, + 0x2d, 0x34, 0x75, 0x60, 0x31, 0x74, 0x2e, 0x0a, 0x4a, 0x11, 0x6c, 0x49, 0x25, 0x01, 0x3a, + 0x3d, 0x22, 0x1e, 0x6d, 0x18, 0x51, 0x78, 0x2d, 0x62, 0x31, 0x4c, 0x50, 0x40, 0x17, 0x4b, + 0x6f, 0x22, 0x00, 0x7f, 0x61, 0x2a, 0x34, 0x3e, 0x00, 0x5f, 0x2f, 0x5f, 0x2f, 0x14, 0x2a, + 0x55, 0x27, 0x1f, 0x46, 0x1f, 0x12, 0x46, 0x5e, 0x1e, 0x0c, 0x7c, 0x38, 0x01, 0x61, 0x64, + 0x76, 0x22, 0x6e, 0x08, 0x20, 0x38, 0x4f, 0x73, 0x72, 0x55, 0x12, 0x42, 0x19, 0x50, 0x61, + 0x43, 0x77, 0x7d, 0x41, 0x2e, 0x35, 0x4f, 0x3d, 0x31, 0x28, 0x58, 0x67, 0x1b, 0x03, 0x51, + 0x20, 0x32, 0x1c, 0x08, 0x6e, 0x37, 0x75, 0x37, 0x44, 0x4f, 0x68, 0x19, 0x07, 0x64, 0x14, + 0x28, 0x25, 0x2b, 0x69, 0x35, 0x18, 0x27, 0x26, 0x14, 0x13, 0x70, 0x42, 0x19, 0x12, 0x75, + 0x3e, 0x02, 0x5d, 0x7c, 0x13, 0x1f, 0x16, 0x53, 0x3b, 0x74, 0x48, 0x3c, 0x5e, 0x39, 0x6c, + 0x1c, 0x1c, 0x74, 0x39, 0x1f, 0x00, 0x1b, 0x06, 0x0a, 0x68, 0x3b, 0x52, 0x4f, 0x1e, 0x6e, + 0x3c, 0x35, 0x0c, 0x38, 0x0e, 0x0b, 0x3b, 0x1a, 0x76, 0x23, 0x29, 0x53, 0x1e, 0x5f, 0x41, + 0x0c, 0x4b, 0x0a, 0x65, 0x28, 0x78, 0x67, 0x48, 0x59, 0x26, 0x6d, 0x31, 0x76, 0x23, 0x70, + 0x61, 0x64, 0x3b, 0x38, 0x79, 0x66, 0x74, 0x53, 0x2c, 0x64, 0x64, 0x54, 0x03, 0x54, 0x65, + 0x44, 0x4c, 0x18, 0x4f, 0x48, 0x20, 0x4f, 0x72, 0x10, 0x3f, 0x0c, 0x52, 0x2d, 0x03, 0x14, + 0x03, 0x51, 0x42, 0x10, 0x77, 0x6a, 0x34, 0x06, 0x32, 0x03, 0x72, 0x14, 0x7c, 0x08, 0x5d, + 0x52, 0x1a, 0x62, 0x7c, 0x3e, 0x30, 0x7e, 0x5f, 0x7f, 0x54, 0x0f, 0x44, 0x49, 0x5d, 0x5e, + 0x10, 0x6a, 0x06, 0x2b, 0x06, 0x53, 0x10, 0x39, 0x37, 0x32, 0x4a, 0x4e, 0x3d, 0x2b, 0x65, + 0x38, 0x39, 0x07, 0x72, 0x54, 0x64, 0x4d, 0x56, 0x6a, 0x03, 0x22, 0x70, 0x7b, 0x5f, 0x60, + 0x0b, 0x2a, 0x0b, 0x6b, 0x10, 0x64, 0x14, 0x05, 0x22, 0x00, 0x73, 0x40, 0x23, 0x5b, 0x51, + 0x1f, 0x2b, 0x1a, 0x5d, 0x69, 0x7a, 0x46, 0x0c, 0x5f, 0x32, 0x4b, 0x4a, 0x28, 0x52, 0x79, + 0x5b, 0x12, 0x42, 0x18, 0x00, 0x5d, 0x27, 0x31, 0x53, 0x3c, 0x4c, 0x36, 0x4e, 0x38, 0x3f, + 0x72, 0x03, 0x71, 0x02, 0x5b, 0x36, 0x59, 0x7f, 0x75, 0x6e, 0x08, 0x54, 0x0d, 0x34, 0x1c, + 0x34, 0x57, 0x5d, 0x69, 0x48, 0x00, 0x3b, 0x05, 0x07, 0x6e, 0x27, 0x65, 0x6e, 0x40, 0x3d, + 0x3a, 0x4f, 0x72, 0x5d, 0x39, 0x16, 0x0f, 0x63, 0x12, 0x12, 0x15, 0x3a, 0x70, 0x0d, 0x57, + 0x18, 0x0d, 0x5e, 0x3d, 0x22, 0x68, 0x68, 0x7c, 0x6d, 0x4f, 0x0c, 0x7b, 0x09, 0x2d, 0x4a, + 0x73, 0x20, 0x47, 0x07, 0x57, 0x75, 0x5d, 0x53, 0x70, 0x34, 0x21, 0x40, 0x57, 0x51, 0x5e, + 0x49, 0x44, 0x00, 0x54, 0x27, 0x04, 0x68, 0x7e, 0x59, 0x56, 0x58, 0x74, 0x14, 0x3c, 0x16, + 0x33, 0x41, 0x16, 0x4b, 0x2f, 0x49, 0x37, 0x0a, 0x54, 0x08, 0x08, 0x1f, 0x39, 0x67, 0x76, + 0x28, 0x28, 0x07, 0x1d, 0x61, 0x47, 0x51, 0x4d, 0x75, 0x26, 0x52, 0x47, 0x47, 0x0c, 0x57, + 0x58, 0x74, 0x3e, 0x62, 0x6c, 0x58, 0x3a, 0x44, 0x1e, 0x16, 0x2e, 0x21, 0x1c, 0x73, 0x45, + 0x67, 0x74, 0x4f, 0x33, 0x66, 0x0e, 0x74, 0x66, 0x26, 0x1f, 0x2e, 0x38, 0x44, 0x40, 0x7e, + 0x2a, 0x50, 0x52, 0x5e, 0x43, 0x01, 0x7a, 0x38, 0x49, 0x3c, 0x55, 0x4d, 0x5a, 0x44, 0x08, + 0x26, 0x59, 0x4d, 0x45, 0x0b, 0x48, 0x0a, 0x33, 0x5e, 0x4a, 0x4d, 0x75, 0x16, 0x17, 0x63, + 0x46, 0x01, 0x2a, 0x55, 0x7b, 0x0f, 0x02, 0x73, 0x6a, 0x4b, 0x7f, 0x75, 0x65, 0x3c, 0x4c, + 0x33, 0x39, 0x6c, 0x74, 0x05, 0x60, 0x0f, 0x7f, 0x2d, 0x41, 0x4d, 0x4d, 0x46, 0x71, 0x09, + 0x6f, 0x4f, 0x60, 0x15, 0x0f, 0x46, 0x73, 0x63, 0x4c, 0x5e, 0x74, 0x30, 0x0d, 0x28, 0x43, + 0x08, 0x72, 0x32, 0x04, 0x2e, 0x31, 0x29, 0x27, 0x44, 0x6d, 0x13, 0x17, 0x48, 0x0f, 0x49, + 0x52, 0x10, 0x13, 0x7f, 0x17, 0x16, 0x62, 0x79, 0x35, 0x78, 0x3e, 0x01, 0x7c, 0x2e, 0x0f, + 0x76, 0x3e, 0x5e, 0x53, 0x6c, 0x5b, 0x5f, 0x7c, 0x19, 0x41, 0x02, 0x2f, 0x17, 0x64, 0x41, + 0x75, 0x10, 0x04, 0x47, 0x7c, 0x3d, 0x4b, 0x52, 0x00, 0x10, 0x5d, 0x51, 0x4e, 0x7a, 0x27, + 0x25, 0x55, 0x40, 0x12, 0x35, 0x60, 0x05, 0x1b, 0x34, 0x2d, 0x04, 0x7a, 0x6a, 0x69, 0x02, + 0x79, 0x03, 0x3a, 0x2f, 0x06, 0x0a, 0x79, 0x7b, 0x12, 0x5d, 0x7c, 0x52, 0x29, 0x47, 0x58, + 0x12, 0x73, 0x3f, 0x27, 0x56, 0x05, 0x0c, 0x48, 0x32, 0x58, 0x6b, 0x57, 0x5c, 0x03, 0x64, + 0x56, 0x11, 0x52, 0x7a, 0x30, 0x36, 0x29, 0x17, 0x3b, 0x68, 0x7a, 0x7c, 0x05, 0x6b, 0x6b, + 0x13, 0x6a, 0x24, 0x5c, 0x68, 0x42, 0x18, 0x32, 0x03, 0x73, 0x6e, 0x04, 0x21, 0x2e, 0x01, + 0x04, 0x63, 0x7d, 0x44, 0x41, 0x12, 0x31, 0x0b, 0x15, 0x1f, 0x70, 0x00, 0x2e, 0x66, 0x14, + 0x3c, 0x7f, 0x2b, 0x00, 0x1f, 0x0c, 0x28, 0x59, 0x0a, 0x16, 0x49, 0x5a, 0x5c, 0x64, 0x65, + 0x4b, 0x11, 0x29, 0x15, 0x36, 0x5a, 0x65, 0x19, 0x4f, 0x60, 0x23, 0x3a, 0x3a, 0x13, 0x25, + 0x02, 0x78, 0x4c, 0x54}; char b64_known[1369] = { - 0x43, 0x7a, 0x42, 0x45, 0x59, 0x6e, 0x77, 0x69, 0x48, 0x77, 0x30, 0x46, 0x5a, 0x79, 0x77, 0x71, 0x4f, 0x53, 0x46, 0x47, 0x43, 0x46, - 0x42, 0x6d, 0x4e, 0x44, 0x63, 0x4c, 0x52, 0x55, 0x73, 0x34, 0x4d, 0x67, 0x5a, 0x36, 0x50, 0x6e, 0x38, 0x4d, 0x51, 0x42, 0x68, 0x72, - 0x4c, 0x57, 0x42, 0x4d, 0x59, 0x41, 0x77, 0x6a, 0x51, 0x7a, 0x73, 0x2b, 0x47, 0x78, 0x59, 0x45, 0x52, 0x6c, 0x67, 0x2f, 0x51, 0x47, - 0x6f, 0x52, 0x42, 0x57, 0x4e, 0x78, 0x46, 0x44, 0x56, 0x48, 0x65, 0x52, 0x4e, 0x76, 0x61, 0x79, 0x63, 0x59, 0x57, 0x30, 0x67, 0x6e, - 0x50, 0x6d, 0x38, 0x56, 0x4d, 0x30, 0x38, 0x2b, 0x58, 0x6c, 0x46, 0x7a, 0x61, 0x43, 0x55, 0x50, 0x42, 0x6c, 0x74, 0x38, 0x63, 0x6e, - 0x55, 0x2b, 0x50, 0x78, 0x74, 0x63, 0x62, 0x57, 0x6f, 0x35, 0x66, 0x47, 0x4e, 0x6a, 0x59, 0x47, 0x78, 0x36, 0x4d, 0x33, 0x5a, 0x53, - 0x45, 0x79, 0x55, 0x7a, 0x66, 0x57, 0x55, 0x6a, 0x4a, 0x78, 0x45, 0x47, 0x42, 0x6b, 0x64, 0x78, 0x48, 0x68, 0x52, 0x30, 0x59, 0x33, - 0x41, 0x74, 0x46, 0x53, 0x63, 0x59, 0x55, 0x51, 0x59, 0x46, 0x4d, 0x78, 0x45, 0x73, 0x61, 0x77, 0x41, 0x74, 0x64, 0x79, 0x42, 0x49, - 0x44, 0x58, 0x4e, 0x52, 0x52, 0x53, 0x56, 0x2f, 0x66, 0x7a, 0x55, 0x6d, 0x4c, 0x69, 0x5a, 0x54, 0x4a, 0x47, 0x67, 0x65, 0x44, 0x6c, - 0x67, 0x36, 0x57, 0x56, 0x42, 0x57, 0x4e, 0x31, 0x39, 0x6d, 0x41, 0x55, 0x78, 0x61, 0x5a, 0x44, 0x4a, 0x51, 0x65, 0x32, 0x6f, 0x67, - 0x63, 0x69, 0x73, 0x64, 0x66, 0x6b, 0x4e, 0x37, 0x59, 0x55, 0x49, 0x4c, 0x59, 0x58, 0x4d, 0x6b, 0x65, 0x54, 0x70, 0x72, 0x53, 0x6e, - 0x6c, 0x75, 0x43, 0x51, 0x38, 0x6e, 0x4c, 0x51, 0x78, 0x65, 0x4d, 0x6b, 0x73, 0x4e, 0x65, 0x55, 0x59, 0x35, 0x49, 0x51, 0x6f, 0x6d, - 0x58, 0x7a, 0x6f, 0x41, 0x4a, 0x6a, 0x38, 0x54, 0x4c, 0x6e, 0x35, 0x51, 0x4b, 0x32, 0x64, 0x47, 0x63, 0x6a, 0x38, 0x37, 0x41, 0x55, - 0x59, 0x62, 0x43, 0x7a, 0x56, 0x4a, 0x4f, 0x52, 0x6c, 0x77, 0x50, 0x51, 0x4a, 0x42, 0x44, 0x6a, 0x67, 0x46, 0x64, 0x6d, 0x56, 0x50, - 0x4d, 0x57, 0x78, 0x65, 0x46, 0x77, 0x51, 0x56, 0x4e, 0x69, 0x5a, 0x6b, 0x4e, 0x42, 0x51, 0x58, 0x66, 0x41, 0x34, 0x4c, 0x57, 0x31, - 0x56, 0x54, 0x61, 0x77, 0x42, 0x43, 0x51, 0x55, 0x38, 0x43, 0x58, 0x42, 0x4d, 0x4b, 0x4c, 0x43, 0x77, 0x2b, 0x45, 0x42, 0x51, 0x7a, - 0x52, 0x58, 0x78, 0x36, 0x57, 0x6a, 0x46, 0x68, 0x4f, 0x51, 0x67, 0x69, 0x61, 0x68, 0x34, 0x50, 0x62, 0x78, 0x74, 0x73, 0x45, 0x31, - 0x35, 0x35, 0x49, 0x48, 0x6c, 0x51, 0x59, 0x67, 0x59, 0x73, 0x64, 0x68, 0x63, 0x45, 0x4b, 0x79, 0x70, 0x31, 0x48, 0x77, 0x77, 0x33, - 0x54, 0x67, 0x39, 0x37, 0x4c, 0x54, 0x52, 0x31, 0x59, 0x44, 0x46, 0x30, 0x4c, 0x67, 0x70, 0x4b, 0x45, 0x57, 0x78, 0x4a, 0x4a, 0x51, - 0x45, 0x36, 0x50, 0x53, 0x49, 0x65, 0x62, 0x52, 0x68, 0x52, 0x65, 0x43, 0x31, 0x69, 0x4d, 0x55, 0x78, 0x51, 0x51, 0x42, 0x64, 0x4c, - 0x62, 0x79, 0x49, 0x41, 0x66, 0x32, 0x45, 0x71, 0x4e, 0x44, 0x34, 0x41, 0x58, 0x79, 0x39, 0x66, 0x4c, 0x78, 0x51, 0x71, 0x56, 0x53, - 0x63, 0x66, 0x52, 0x68, 0x38, 0x53, 0x52, 0x6c, 0x34, 0x65, 0x44, 0x48, 0x77, 0x34, 0x41, 0x57, 0x46, 0x6b, 0x64, 0x69, 0x4a, 0x75, - 0x43, 0x43, 0x41, 0x34, 0x54, 0x33, 0x4e, 0x79, 0x56, 0x52, 0x4a, 0x43, 0x47, 0x56, 0x42, 0x68, 0x51, 0x33, 0x64, 0x39, 0x51, 0x53, - 0x34, 0x31, 0x54, 0x7a, 0x30, 0x78, 0x4b, 0x46, 0x68, 0x6e, 0x47, 0x77, 0x4e, 0x52, 0x49, 0x44, 0x49, 0x63, 0x43, 0x47, 0x34, 0x33, - 0x64, 0x54, 0x64, 0x45, 0x54, 0x32, 0x67, 0x5a, 0x42, 0x32, 0x51, 0x55, 0x4b, 0x43, 0x55, 0x72, 0x61, 0x54, 0x55, 0x59, 0x4a, 0x79, - 0x59, 0x55, 0x45, 0x33, 0x42, 0x43, 0x47, 0x52, 0x4a, 0x31, 0x50, 0x67, 0x4a, 0x64, 0x66, 0x42, 0x4d, 0x66, 0x46, 0x6c, 0x4d, 0x37, - 0x64, 0x45, 0x67, 0x38, 0x58, 0x6a, 0x6c, 0x73, 0x48, 0x42, 0x78, 0x30, 0x4f, 0x52, 0x38, 0x41, 0x47, 0x77, 0x59, 0x4b, 0x61, 0x44, - 0x74, 0x53, 0x54, 0x78, 0x35, 0x75, 0x50, 0x44, 0x55, 0x4d, 0x4f, 0x41, 0x34, 0x4c, 0x4f, 0x78, 0x70, 0x32, 0x49, 0x79, 0x6c, 0x54, - 0x48, 0x6c, 0x39, 0x42, 0x44, 0x45, 0x73, 0x4b, 0x5a, 0x53, 0x68, 0x34, 0x5a, 0x30, 0x68, 0x5a, 0x4a, 0x6d, 0x30, 0x78, 0x64, 0x69, - 0x4e, 0x77, 0x59, 0x57, 0x51, 0x37, 0x4f, 0x48, 0x6c, 0x6d, 0x64, 0x46, 0x4d, 0x73, 0x5a, 0x47, 0x52, 0x55, 0x41, 0x31, 0x52, 0x6c, - 0x52, 0x45, 0x77, 0x59, 0x54, 0x30, 0x67, 0x67, 0x54, 0x33, 0x49, 0x51, 0x50, 0x77, 0x78, 0x53, 0x4c, 0x51, 0x4d, 0x55, 0x41, 0x31, - 0x46, 0x43, 0x45, 0x48, 0x64, 0x71, 0x4e, 0x41, 0x59, 0x79, 0x41, 0x33, 0x49, 0x55, 0x66, 0x41, 0x68, 0x64, 0x55, 0x68, 0x70, 0x69, - 0x66, 0x44, 0x34, 0x77, 0x66, 0x6c, 0x39, 0x2f, 0x56, 0x41, 0x39, 0x45, 0x53, 0x56, 0x31, 0x65, 0x45, 0x47, 0x6f, 0x47, 0x4b, 0x77, - 0x5a, 0x54, 0x45, 0x44, 0x6b, 0x33, 0x4d, 0x6b, 0x70, 0x4f, 0x50, 0x53, 0x74, 0x6c, 0x4f, 0x44, 0x6b, 0x48, 0x63, 0x6c, 0x52, 0x6b, - 0x54, 0x56, 0x5a, 0x71, 0x41, 0x79, 0x4a, 0x77, 0x65, 0x31, 0x39, 0x67, 0x43, 0x79, 0x6f, 0x4c, 0x61, 0x78, 0x42, 0x6b, 0x46, 0x41, - 0x55, 0x69, 0x41, 0x48, 0x4e, 0x41, 0x49, 0x31, 0x74, 0x52, 0x48, 0x79, 0x73, 0x61, 0x58, 0x57, 0x6c, 0x36, 0x52, 0x67, 0x78, 0x66, - 0x4d, 0x6b, 0x74, 0x4b, 0x4b, 0x46, 0x4a, 0x35, 0x57, 0x78, 0x4a, 0x43, 0x47, 0x41, 0x42, 0x64, 0x4a, 0x7a, 0x46, 0x54, 0x50, 0x45, - 0x77, 0x32, 0x54, 0x6a, 0x67, 0x2f, 0x63, 0x67, 0x4e, 0x78, 0x41, 0x6c, 0x73, 0x32, 0x57, 0x58, 0x39, 0x31, 0x62, 0x67, 0x68, 0x55, - 0x44, 0x54, 0x51, 0x63, 0x4e, 0x46, 0x64, 0x64, 0x61, 0x55, 0x67, 0x41, 0x4f, 0x77, 0x55, 0x48, 0x62, 0x69, 0x64, 0x6c, 0x62, 0x6b, - 0x41, 0x39, 0x4f, 0x6b, 0x39, 0x79, 0x58, 0x54, 0x6b, 0x57, 0x44, 0x32, 0x4d, 0x53, 0x45, 0x68, 0x55, 0x36, 0x63, 0x41, 0x31, 0x58, - 0x47, 0x41, 0x31, 0x65, 0x50, 0x53, 0x4a, 0x6f, 0x61, 0x48, 0x78, 0x74, 0x54, 0x77, 0x78, 0x37, 0x43, 0x53, 0x31, 0x4b, 0x63, 0x79, - 0x42, 0x48, 0x42, 0x31, 0x64, 0x31, 0x58, 0x56, 0x4e, 0x77, 0x4e, 0x43, 0x46, 0x41, 0x56, 0x31, 0x46, 0x65, 0x53, 0x55, 0x51, 0x41, - 0x56, 0x43, 0x63, 0x45, 0x61, 0x48, 0x35, 0x5a, 0x56, 0x6c, 0x68, 0x30, 0x46, 0x44, 0x77, 0x57, 0x4d, 0x30, 0x45, 0x57, 0x53, 0x79, - 0x39, 0x4a, 0x4e, 0x77, 0x70, 0x55, 0x43, 0x41, 0x67, 0x66, 0x4f, 0x57, 0x64, 0x32, 0x4b, 0x43, 0x67, 0x48, 0x48, 0x57, 0x46, 0x48, - 0x55, 0x55, 0x31, 0x31, 0x4a, 0x6c, 0x4a, 0x48, 0x52, 0x77, 0x78, 0x58, 0x57, 0x48, 0x51, 0x2b, 0x59, 0x6d, 0x78, 0x59, 0x4f, 0x6b, - 0x51, 0x65, 0x46, 0x69, 0x34, 0x68, 0x48, 0x48, 0x4e, 0x46, 0x5a, 0x33, 0x52, 0x50, 0x4d, 0x32, 0x59, 0x4f, 0x64, 0x47, 0x59, 0x6d, - 0x48, 0x79, 0x34, 0x34, 0x52, 0x45, 0x42, 0x2b, 0x4b, 0x6c, 0x42, 0x53, 0x58, 0x6b, 0x4d, 0x42, 0x65, 0x6a, 0x68, 0x4a, 0x50, 0x46, - 0x56, 0x4e, 0x57, 0x6b, 0x51, 0x49, 0x4a, 0x6c, 0x6c, 0x4e, 0x52, 0x51, 0x74, 0x49, 0x43, 0x6a, 0x4e, 0x65, 0x53, 0x6b, 0x31, 0x31, - 0x46, 0x68, 0x64, 0x6a, 0x52, 0x67, 0x45, 0x71, 0x56, 0x58, 0x73, 0x50, 0x41, 0x6e, 0x4e, 0x71, 0x53, 0x33, 0x39, 0x31, 0x5a, 0x54, - 0x78, 0x4d, 0x4d, 0x7a, 0x6c, 0x73, 0x64, 0x41, 0x56, 0x67, 0x44, 0x33, 0x38, 0x74, 0x51, 0x55, 0x31, 0x4e, 0x52, 0x6e, 0x45, 0x4a, - 0x62, 0x30, 0x39, 0x67, 0x46, 0x51, 0x39, 0x47, 0x63, 0x32, 0x4e, 0x4d, 0x58, 0x6e, 0x51, 0x77, 0x44, 0x53, 0x68, 0x44, 0x43, 0x48, - 0x49, 0x79, 0x42, 0x43, 0x34, 0x78, 0x4b, 0x53, 0x64, 0x45, 0x62, 0x52, 0x4d, 0x58, 0x53, 0x41, 0x39, 0x4a, 0x55, 0x68, 0x41, 0x54, - 0x66, 0x78, 0x63, 0x57, 0x59, 0x6e, 0x6b, 0x31, 0x65, 0x44, 0x34, 0x42, 0x66, 0x43, 0x34, 0x50, 0x64, 0x6a, 0x35, 0x65, 0x55, 0x32, - 0x78, 0x62, 0x58, 0x33, 0x77, 0x5a, 0x51, 0x51, 0x49, 0x76, 0x46, 0x32, 0x52, 0x42, 0x64, 0x52, 0x41, 0x45, 0x52, 0x33, 0x77, 0x39, - 0x53, 0x31, 0x49, 0x41, 0x45, 0x46, 0x31, 0x52, 0x54, 0x6e, 0x6f, 0x6e, 0x4a, 0x56, 0x56, 0x41, 0x45, 0x6a, 0x56, 0x67, 0x42, 0x52, - 0x73, 0x30, 0x4c, 0x51, 0x52, 0x36, 0x61, 0x6d, 0x6b, 0x43, 0x65, 0x51, 0x4d, 0x36, 0x4c, 0x77, 0x59, 0x4b, 0x65, 0x58, 0x73, 0x53, - 0x58, 0x58, 0x78, 0x53, 0x4b, 0x55, 0x64, 0x59, 0x45, 0x6e, 0x4d, 0x2f, 0x4a, 0x31, 0x59, 0x46, 0x44, 0x45, 0x67, 0x79, 0x57, 0x47, - 0x74, 0x58, 0x58, 0x41, 0x4e, 0x6b, 0x56, 0x68, 0x46, 0x53, 0x65, 0x6a, 0x41, 0x32, 0x4b, 0x52, 0x63, 0x37, 0x61, 0x48, 0x70, 0x38, - 0x42, 0x57, 0x74, 0x72, 0x45, 0x32, 0x6f, 0x6b, 0x58, 0x47, 0x68, 0x43, 0x47, 0x44, 0x49, 0x44, 0x63, 0x32, 0x34, 0x45, 0x49, 0x53, - 0x34, 0x42, 0x42, 0x47, 0x4e, 0x39, 0x52, 0x45, 0x45, 0x53, 0x4d, 0x51, 0x73, 0x56, 0x48, 0x33, 0x41, 0x41, 0x4c, 0x6d, 0x59, 0x55, - 0x50, 0x48, 0x38, 0x72, 0x41, 0x42, 0x38, 0x4d, 0x4b, 0x46, 0x6b, 0x4b, 0x46, 0x6b, 0x6c, 0x61, 0x58, 0x47, 0x52, 0x6c, 0x53, 0x78, - 0x45, 0x70, 0x46, 0x54, 0x5a, 0x61, 0x5a, 0x52, 0x6c, 0x50, 0x59, 0x43, 0x4d, 0x36, 0x4f, 0x68, 0x4d, 0x6c, 0x41, 0x6e, 0x68, 0x4d, - 0x56, 0x41, 0x3d, 0x3d, 0x00}; + 0x43, 0x7a, 0x42, 0x45, 0x59, 0x6e, 0x77, 0x69, 0x48, 0x77, 0x30, 0x46, 0x5a, 0x79, 0x77, + 0x71, 0x4f, 0x53, 0x46, 0x47, 0x43, 0x46, 0x42, 0x6d, 0x4e, 0x44, 0x63, 0x4c, 0x52, 0x55, + 0x73, 0x34, 0x4d, 0x67, 0x5a, 0x36, 0x50, 0x6e, 0x38, 0x4d, 0x51, 0x42, 0x68, 0x72, 0x4c, + 0x57, 0x42, 0x4d, 0x59, 0x41, 0x77, 0x6a, 0x51, 0x7a, 0x73, 0x2b, 0x47, 0x78, 0x59, 0x45, + 0x52, 0x6c, 0x67, 0x2f, 0x51, 0x47, 0x6f, 0x52, 0x42, 0x57, 0x4e, 0x78, 0x46, 0x44, 0x56, + 0x48, 0x65, 0x52, 0x4e, 0x76, 0x61, 0x79, 0x63, 0x59, 0x57, 0x30, 0x67, 0x6e, 0x50, 0x6d, + 0x38, 0x56, 0x4d, 0x30, 0x38, 0x2b, 0x58, 0x6c, 0x46, 0x7a, 0x61, 0x43, 0x55, 0x50, 0x42, + 0x6c, 0x74, 0x38, 0x63, 0x6e, 0x55, 0x2b, 0x50, 0x78, 0x74, 0x63, 0x62, 0x57, 0x6f, 0x35, + 0x66, 0x47, 0x4e, 0x6a, 0x59, 0x47, 0x78, 0x36, 0x4d, 0x33, 0x5a, 0x53, 0x45, 0x79, 0x55, + 0x7a, 0x66, 0x57, 0x55, 0x6a, 0x4a, 0x78, 0x45, 0x47, 0x42, 0x6b, 0x64, 0x78, 0x48, 0x68, + 0x52, 0x30, 0x59, 0x33, 0x41, 0x74, 0x46, 0x53, 0x63, 0x59, 0x55, 0x51, 0x59, 0x46, 0x4d, + 0x78, 0x45, 0x73, 0x61, 0x77, 0x41, 0x74, 0x64, 0x79, 0x42, 0x49, 0x44, 0x58, 0x4e, 0x52, + 0x52, 0x53, 0x56, 0x2f, 0x66, 0x7a, 0x55, 0x6d, 0x4c, 0x69, 0x5a, 0x54, 0x4a, 0x47, 0x67, + 0x65, 0x44, 0x6c, 0x67, 0x36, 0x57, 0x56, 0x42, 0x57, 0x4e, 0x31, 0x39, 0x6d, 0x41, 0x55, + 0x78, 0x61, 0x5a, 0x44, 0x4a, 0x51, 0x65, 0x32, 0x6f, 0x67, 0x63, 0x69, 0x73, 0x64, 0x66, + 0x6b, 0x4e, 0x37, 0x59, 0x55, 0x49, 0x4c, 0x59, 0x58, 0x4d, 0x6b, 0x65, 0x54, 0x70, 0x72, + 0x53, 0x6e, 0x6c, 0x75, 0x43, 0x51, 0x38, 0x6e, 0x4c, 0x51, 0x78, 0x65, 0x4d, 0x6b, 0x73, + 0x4e, 0x65, 0x55, 0x59, 0x35, 0x49, 0x51, 0x6f, 0x6d, 0x58, 0x7a, 0x6f, 0x41, 0x4a, 0x6a, + 0x38, 0x54, 0x4c, 0x6e, 0x35, 0x51, 0x4b, 0x32, 0x64, 0x47, 0x63, 0x6a, 0x38, 0x37, 0x41, + 0x55, 0x59, 0x62, 0x43, 0x7a, 0x56, 0x4a, 0x4f, 0x52, 0x6c, 0x77, 0x50, 0x51, 0x4a, 0x42, + 0x44, 0x6a, 0x67, 0x46, 0x64, 0x6d, 0x56, 0x50, 0x4d, 0x57, 0x78, 0x65, 0x46, 0x77, 0x51, + 0x56, 0x4e, 0x69, 0x5a, 0x6b, 0x4e, 0x42, 0x51, 0x58, 0x66, 0x41, 0x34, 0x4c, 0x57, 0x31, + 0x56, 0x54, 0x61, 0x77, 0x42, 0x43, 0x51, 0x55, 0x38, 0x43, 0x58, 0x42, 0x4d, 0x4b, 0x4c, + 0x43, 0x77, 0x2b, 0x45, 0x42, 0x51, 0x7a, 0x52, 0x58, 0x78, 0x36, 0x57, 0x6a, 0x46, 0x68, + 0x4f, 0x51, 0x67, 0x69, 0x61, 0x68, 0x34, 0x50, 0x62, 0x78, 0x74, 0x73, 0x45, 0x31, 0x35, + 0x35, 0x49, 0x48, 0x6c, 0x51, 0x59, 0x67, 0x59, 0x73, 0x64, 0x68, 0x63, 0x45, 0x4b, 0x79, + 0x70, 0x31, 0x48, 0x77, 0x77, 0x33, 0x54, 0x67, 0x39, 0x37, 0x4c, 0x54, 0x52, 0x31, 0x59, + 0x44, 0x46, 0x30, 0x4c, 0x67, 0x70, 0x4b, 0x45, 0x57, 0x78, 0x4a, 0x4a, 0x51, 0x45, 0x36, + 0x50, 0x53, 0x49, 0x65, 0x62, 0x52, 0x68, 0x52, 0x65, 0x43, 0x31, 0x69, 0x4d, 0x55, 0x78, + 0x51, 0x51, 0x42, 0x64, 0x4c, 0x62, 0x79, 0x49, 0x41, 0x66, 0x32, 0x45, 0x71, 0x4e, 0x44, + 0x34, 0x41, 0x58, 0x79, 0x39, 0x66, 0x4c, 0x78, 0x51, 0x71, 0x56, 0x53, 0x63, 0x66, 0x52, + 0x68, 0x38, 0x53, 0x52, 0x6c, 0x34, 0x65, 0x44, 0x48, 0x77, 0x34, 0x41, 0x57, 0x46, 0x6b, + 0x64, 0x69, 0x4a, 0x75, 0x43, 0x43, 0x41, 0x34, 0x54, 0x33, 0x4e, 0x79, 0x56, 0x52, 0x4a, + 0x43, 0x47, 0x56, 0x42, 0x68, 0x51, 0x33, 0x64, 0x39, 0x51, 0x53, 0x34, 0x31, 0x54, 0x7a, + 0x30, 0x78, 0x4b, 0x46, 0x68, 0x6e, 0x47, 0x77, 0x4e, 0x52, 0x49, 0x44, 0x49, 0x63, 0x43, + 0x47, 0x34, 0x33, 0x64, 0x54, 0x64, 0x45, 0x54, 0x32, 0x67, 0x5a, 0x42, 0x32, 0x51, 0x55, + 0x4b, 0x43, 0x55, 0x72, 0x61, 0x54, 0x55, 0x59, 0x4a, 0x79, 0x59, 0x55, 0x45, 0x33, 0x42, + 0x43, 0x47, 0x52, 0x4a, 0x31, 0x50, 0x67, 0x4a, 0x64, 0x66, 0x42, 0x4d, 0x66, 0x46, 0x6c, + 0x4d, 0x37, 0x64, 0x45, 0x67, 0x38, 0x58, 0x6a, 0x6c, 0x73, 0x48, 0x42, 0x78, 0x30, 0x4f, + 0x52, 0x38, 0x41, 0x47, 0x77, 0x59, 0x4b, 0x61, 0x44, 0x74, 0x53, 0x54, 0x78, 0x35, 0x75, + 0x50, 0x44, 0x55, 0x4d, 0x4f, 0x41, 0x34, 0x4c, 0x4f, 0x78, 0x70, 0x32, 0x49, 0x79, 0x6c, + 0x54, 0x48, 0x6c, 0x39, 0x42, 0x44, 0x45, 0x73, 0x4b, 0x5a, 0x53, 0x68, 0x34, 0x5a, 0x30, + 0x68, 0x5a, 0x4a, 0x6d, 0x30, 0x78, 0x64, 0x69, 0x4e, 0x77, 0x59, 0x57, 0x51, 0x37, 0x4f, + 0x48, 0x6c, 0x6d, 0x64, 0x46, 0x4d, 0x73, 0x5a, 0x47, 0x52, 0x55, 0x41, 0x31, 0x52, 0x6c, + 0x52, 0x45, 0x77, 0x59, 0x54, 0x30, 0x67, 0x67, 0x54, 0x33, 0x49, 0x51, 0x50, 0x77, 0x78, + 0x53, 0x4c, 0x51, 0x4d, 0x55, 0x41, 0x31, 0x46, 0x43, 0x45, 0x48, 0x64, 0x71, 0x4e, 0x41, + 0x59, 0x79, 0x41, 0x33, 0x49, 0x55, 0x66, 0x41, 0x68, 0x64, 0x55, 0x68, 0x70, 0x69, 0x66, + 0x44, 0x34, 0x77, 0x66, 0x6c, 0x39, 0x2f, 0x56, 0x41, 0x39, 0x45, 0x53, 0x56, 0x31, 0x65, + 0x45, 0x47, 0x6f, 0x47, 0x4b, 0x77, 0x5a, 0x54, 0x45, 0x44, 0x6b, 0x33, 0x4d, 0x6b, 0x70, + 0x4f, 0x50, 0x53, 0x74, 0x6c, 0x4f, 0x44, 0x6b, 0x48, 0x63, 0x6c, 0x52, 0x6b, 0x54, 0x56, + 0x5a, 0x71, 0x41, 0x79, 0x4a, 0x77, 0x65, 0x31, 0x39, 0x67, 0x43, 0x79, 0x6f, 0x4c, 0x61, + 0x78, 0x42, 0x6b, 0x46, 0x41, 0x55, 0x69, 0x41, 0x48, 0x4e, 0x41, 0x49, 0x31, 0x74, 0x52, + 0x48, 0x79, 0x73, 0x61, 0x58, 0x57, 0x6c, 0x36, 0x52, 0x67, 0x78, 0x66, 0x4d, 0x6b, 0x74, + 0x4b, 0x4b, 0x46, 0x4a, 0x35, 0x57, 0x78, 0x4a, 0x43, 0x47, 0x41, 0x42, 0x64, 0x4a, 0x7a, + 0x46, 0x54, 0x50, 0x45, 0x77, 0x32, 0x54, 0x6a, 0x67, 0x2f, 0x63, 0x67, 0x4e, 0x78, 0x41, + 0x6c, 0x73, 0x32, 0x57, 0x58, 0x39, 0x31, 0x62, 0x67, 0x68, 0x55, 0x44, 0x54, 0x51, 0x63, + 0x4e, 0x46, 0x64, 0x64, 0x61, 0x55, 0x67, 0x41, 0x4f, 0x77, 0x55, 0x48, 0x62, 0x69, 0x64, + 0x6c, 0x62, 0x6b, 0x41, 0x39, 0x4f, 0x6b, 0x39, 0x79, 0x58, 0x54, 0x6b, 0x57, 0x44, 0x32, + 0x4d, 0x53, 0x45, 0x68, 0x55, 0x36, 0x63, 0x41, 0x31, 0x58, 0x47, 0x41, 0x31, 0x65, 0x50, + 0x53, 0x4a, 0x6f, 0x61, 0x48, 0x78, 0x74, 0x54, 0x77, 0x78, 0x37, 0x43, 0x53, 0x31, 0x4b, + 0x63, 0x79, 0x42, 0x48, 0x42, 0x31, 0x64, 0x31, 0x58, 0x56, 0x4e, 0x77, 0x4e, 0x43, 0x46, + 0x41, 0x56, 0x31, 0x46, 0x65, 0x53, 0x55, 0x51, 0x41, 0x56, 0x43, 0x63, 0x45, 0x61, 0x48, + 0x35, 0x5a, 0x56, 0x6c, 0x68, 0x30, 0x46, 0x44, 0x77, 0x57, 0x4d, 0x30, 0x45, 0x57, 0x53, + 0x79, 0x39, 0x4a, 0x4e, 0x77, 0x70, 0x55, 0x43, 0x41, 0x67, 0x66, 0x4f, 0x57, 0x64, 0x32, + 0x4b, 0x43, 0x67, 0x48, 0x48, 0x57, 0x46, 0x48, 0x55, 0x55, 0x31, 0x31, 0x4a, 0x6c, 0x4a, + 0x48, 0x52, 0x77, 0x78, 0x58, 0x57, 0x48, 0x51, 0x2b, 0x59, 0x6d, 0x78, 0x59, 0x4f, 0x6b, + 0x51, 0x65, 0x46, 0x69, 0x34, 0x68, 0x48, 0x48, 0x4e, 0x46, 0x5a, 0x33, 0x52, 0x50, 0x4d, + 0x32, 0x59, 0x4f, 0x64, 0x47, 0x59, 0x6d, 0x48, 0x79, 0x34, 0x34, 0x52, 0x45, 0x42, 0x2b, + 0x4b, 0x6c, 0x42, 0x53, 0x58, 0x6b, 0x4d, 0x42, 0x65, 0x6a, 0x68, 0x4a, 0x50, 0x46, 0x56, + 0x4e, 0x57, 0x6b, 0x51, 0x49, 0x4a, 0x6c, 0x6c, 0x4e, 0x52, 0x51, 0x74, 0x49, 0x43, 0x6a, + 0x4e, 0x65, 0x53, 0x6b, 0x31, 0x31, 0x46, 0x68, 0x64, 0x6a, 0x52, 0x67, 0x45, 0x71, 0x56, + 0x58, 0x73, 0x50, 0x41, 0x6e, 0x4e, 0x71, 0x53, 0x33, 0x39, 0x31, 0x5a, 0x54, 0x78, 0x4d, + 0x4d, 0x7a, 0x6c, 0x73, 0x64, 0x41, 0x56, 0x67, 0x44, 0x33, 0x38, 0x74, 0x51, 0x55, 0x31, + 0x4e, 0x52, 0x6e, 0x45, 0x4a, 0x62, 0x30, 0x39, 0x67, 0x46, 0x51, 0x39, 0x47, 0x63, 0x32, + 0x4e, 0x4d, 0x58, 0x6e, 0x51, 0x77, 0x44, 0x53, 0x68, 0x44, 0x43, 0x48, 0x49, 0x79, 0x42, + 0x43, 0x34, 0x78, 0x4b, 0x53, 0x64, 0x45, 0x62, 0x52, 0x4d, 0x58, 0x53, 0x41, 0x39, 0x4a, + 0x55, 0x68, 0x41, 0x54, 0x66, 0x78, 0x63, 0x57, 0x59, 0x6e, 0x6b, 0x31, 0x65, 0x44, 0x34, + 0x42, 0x66, 0x43, 0x34, 0x50, 0x64, 0x6a, 0x35, 0x65, 0x55, 0x32, 0x78, 0x62, 0x58, 0x33, + 0x77, 0x5a, 0x51, 0x51, 0x49, 0x76, 0x46, 0x32, 0x52, 0x42, 0x64, 0x52, 0x41, 0x45, 0x52, + 0x33, 0x77, 0x39, 0x53, 0x31, 0x49, 0x41, 0x45, 0x46, 0x31, 0x52, 0x54, 0x6e, 0x6f, 0x6e, + 0x4a, 0x56, 0x56, 0x41, 0x45, 0x6a, 0x56, 0x67, 0x42, 0x52, 0x73, 0x30, 0x4c, 0x51, 0x52, + 0x36, 0x61, 0x6d, 0x6b, 0x43, 0x65, 0x51, 0x4d, 0x36, 0x4c, 0x77, 0x59, 0x4b, 0x65, 0x58, + 0x73, 0x53, 0x58, 0x58, 0x78, 0x53, 0x4b, 0x55, 0x64, 0x59, 0x45, 0x6e, 0x4d, 0x2f, 0x4a, + 0x31, 0x59, 0x46, 0x44, 0x45, 0x67, 0x79, 0x57, 0x47, 0x74, 0x58, 0x58, 0x41, 0x4e, 0x6b, + 0x56, 0x68, 0x46, 0x53, 0x65, 0x6a, 0x41, 0x32, 0x4b, 0x52, 0x63, 0x37, 0x61, 0x48, 0x70, + 0x38, 0x42, 0x57, 0x74, 0x72, 0x45, 0x32, 0x6f, 0x6b, 0x58, 0x47, 0x68, 0x43, 0x47, 0x44, + 0x49, 0x44, 0x63, 0x32, 0x34, 0x45, 0x49, 0x53, 0x34, 0x42, 0x42, 0x47, 0x4e, 0x39, 0x52, + 0x45, 0x45, 0x53, 0x4d, 0x51, 0x73, 0x56, 0x48, 0x33, 0x41, 0x41, 0x4c, 0x6d, 0x59, 0x55, + 0x50, 0x48, 0x38, 0x72, 0x41, 0x42, 0x38, 0x4d, 0x4b, 0x46, 0x6b, 0x4b, 0x46, 0x6b, 0x6c, + 0x61, 0x58, 0x47, 0x52, 0x6c, 0x53, 0x78, 0x45, 0x70, 0x46, 0x54, 0x5a, 0x61, 0x5a, 0x52, + 0x6c, 0x50, 0x59, 0x43, 0x4d, 0x36, 0x4f, 0x68, 0x4d, 0x6c, 0x41, 0x6e, 0x68, 0x4d, 0x56, + 0x41, 0x3d, 0x3d, 0x00}; char *b64_test; plan_tests(1); diff --git a/lib/tests/test_cmd.c b/lib/tests/test_cmd.c index c8867dfb..ade0da90 100644 --- a/lib/tests/test_cmd.c +++ b/lib/tests/test_cmd.c @@ -67,7 +67,8 @@ int main(int argc, char **argv) { result = cmd_run_array(command_line, &chld_out, &chld_err, 0); ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); - ok(strcmp(chld_out.line[0], "this is test one") == 0, "(array) Check for expected stdout output"); + ok(strcmp(chld_out.line[0], "this is test one") == 0, + "(array) Check for expected stdout output"); ok(result == 0, "(array) Checking exit code"); /* ensure everything is empty again */ @@ -82,7 +83,8 @@ int main(int argc, char **argv) { ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); - ok(strcmp(chld_out.line[0], "this is test one") == 0, "(string) Check for expected stdout output"); + ok(strcmp(chld_out.line[0], "this is test one") == 0, + "(string) Check for expected stdout output"); ok(result == 0, "(string) Checking exit code"); diag("Running plain echo command, set two"); @@ -104,7 +106,8 @@ int main(int argc, char **argv) { result = cmd_run_array(command_line, &chld_out, &chld_err, 0); ok(chld_out.lines == 1, "(array) Check for expected number of stdout lines"); ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); - ok(strcmp(chld_out.line[0], "this is test two") == 0, "(array) Check for expected stdout output"); + ok(strcmp(chld_out.line[0], "this is test two") == 0, + "(array) Check for expected stdout output"); ok(result == 0, "(array) Checking exit code"); /* ensure everything is empty again */ @@ -119,7 +122,8 @@ int main(int argc, char **argv) { ok(chld_out.lines == 1, "(string) Check for expected number of stdout lines"); ok(chld_err.lines == 0, "(string) Check for expected number of stderr lines"); - ok(strcmp(chld_out.line[0], "this is test one") == 0, "(string) Check for expected stdout output"); + ok(strcmp(chld_out.line[0], "this is test one") == 0, + "(string) Check for expected stdout output"); ok(result == 0, "(string) Checking exit code"); /* ensure everything is empty again */ @@ -130,7 +134,8 @@ int main(int argc, char **argv) { ok(chld_err.lines == 0, "(initialised) Checking stderr is reset"); ok(result == UNSET, "(initialised) Checking exit code is reset"); - /* Pass linefeeds via parameters through - those should be evaluated by echo to give multi line output */ + /* Pass linefeeds via parameters through - those should be evaluated by echo to give multi line + * output */ command_line[0] = strdup("/bin/echo"); command_line[1] = strdup("this is a test via echo\nline two\nit's line 3"); command_line[2] = strdup("and (note space between '3' and 'and') $$ will not get evaluated"); @@ -138,9 +143,12 @@ int main(int argc, char **argv) { result = cmd_run_array(command_line, &chld_out, &chld_err, 0); ok(chld_out.lines == 3, "(array) Check for expected number of stdout lines"); ok(chld_err.lines == 0, "(array) Check for expected number of stderr lines"); - ok(strcmp(chld_out.line[0], "this is a test via echo") == 0, "(array) Check line 1 for expected stdout output"); - ok(strcmp(chld_out.line[1], "line two") == 0, "(array) Check line 2 for expected stdout output"); - ok(strcmp(chld_out.line[2], "it's line 3 and (note space between '3' and 'and') $$ will not get evaluated") == 0, + ok(strcmp(chld_out.line[0], "this is a test via echo") == 0, + "(array) Check line 1 for expected stdout output"); + ok(strcmp(chld_out.line[1], "line two") == 0, + "(array) Check line 2 for expected stdout output"); + ok(strcmp(chld_out.line[2], + "it's line 3 and (note space between '3' and 'and') $$ will not get evaluated") == 0, "(array) Check line 3 for expected stdout output"); ok(result == 0, "(array) Checking exit code"); @@ -171,7 +179,8 @@ int main(int argc, char **argv) { ok(chld_out.lines == 0, "/bin/sh returns no stdout when file is missing..."); ok(chld_err.lines == 1, "...but does give an error line"); - ok(strstr(chld_err.line[0], "non-existent-file") != NULL, "And missing filename is in error message"); + ok(strstr(chld_err.line[0], "non-existent-file") != NULL, + "And missing filename is in error message"); ok(result != 0, "Get non-zero return code from /bin/sh"); /* ensure everything is empty again */ diff --git a/lib/tests/test_generic_output.c b/lib/tests/test_generic_output.c index e67aefc9..e4a78bcd 100644 --- a/lib/tests/test_generic_output.c +++ b/lib/tests/test_generic_output.c @@ -110,7 +110,8 @@ void test_two_subchecks(void) { sc1.output = "foobar"; sc1 = mp_set_subcheck_state(sc1, STATE_WARNING); - ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state directly after setting it"); + ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, + "Test subcheck state directly after setting it"); mp_perfdata pd1 = perfdata_init(); @@ -129,7 +130,8 @@ void test_two_subchecks(void) { mp_add_subcheck_to_subcheck(&sc1, sc2); - ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, "Test subcheck state after adding a subcheck"); + ok(mp_compute_subcheck_state(sc1) == STATE_WARNING, + "Test subcheck state after adding a subcheck"); mp_check check = mp_check_init(); mp_add_subcheck_to_check(&check, sc1); diff --git a/lib/tests/test_ini1.c b/lib/tests/test_ini1.c index 246c1250..3792d142 100644 --- a/lib/tests/test_ini1.c +++ b/lib/tests/test_ini1.c @@ -42,8 +42,9 @@ char *list2str(np_arg_list *optlst) { free(optltmp); } /* Strip last whitespace */ - if (strlen(optstr) > 1) + if (strlen(optstr) > 1) { optstr[strlen(optstr) - 1] = '\0'; + } return optstr; } @@ -54,15 +55,18 @@ int main(int argc, char **argv) { plan_tests(12); optstr = list2str(np_get_defaults("section@./config-tiny.ini", "check_disk")); - ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), "config-tiny.ini's section as expected"); + ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), + "config-tiny.ini's section as expected"); my_free(optstr); optstr = list2str(np_get_defaults("@./config-tiny.ini", "section")); - ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), "Used default section name, without specific"); + ok(!strcmp(optstr, "--one=two --Foo=Bar --this=Your Mother! --blank"), + "Used default section name, without specific"); my_free(optstr); optstr = list2str(np_get_defaults("Section Two@./config-tiny.ini", "check_disk")); - ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), "config-tiny.ini's Section Two as expected"); + ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), + "config-tiny.ini's Section Two as expected"); my_free(optstr); optstr = list2str(np_get_defaults("/path/to/file.txt@./config-tiny.ini", "check_disk")); @@ -70,15 +74,18 @@ int main(int argc, char **argv) { my_free(optstr); optstr = list2str(np_get_defaults("section2@./config-tiny.ini", "check_disk")); - ok(!strcmp(optstr, "--this=that"), "config-tiny.ini's section2 with whitespace before section name"); + ok(!strcmp(optstr, "--this=that"), + "config-tiny.ini's section2 with whitespace before section name"); my_free(optstr); optstr = list2str(np_get_defaults("section3@./config-tiny.ini", "check_disk")); - ok(!strcmp(optstr, "--this=that"), "config-tiny.ini's section3 with whitespace after section name"); + ok(!strcmp(optstr, "--this=that"), + "config-tiny.ini's section3 with whitespace after section name"); my_free(optstr); optstr = list2str(np_get_defaults("check_mysql@./plugin.ini", "check_disk")); - ok(!strcmp(optstr, "--username=operator --password=secret"), "plugin.ini's check_mysql as expected"); + ok(!strcmp(optstr, "--username=operator --password=secret"), + "plugin.ini's check_mysql as expected"); my_free(optstr); optstr = list2str(np_get_defaults("check_mysql2@./plugin.ini", "check_disk")); @@ -90,29 +97,39 @@ int main(int argc, char **argv) { my_free(optstr); optstr = list2str(np_get_defaults("Section Two@./config-dos.ini", "check_disk")); - ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), "config-dos.ini's Section Two as expected"); + ok(!strcmp(optstr, "--something else=blah --remove=whitespace"), + "config-dos.ini's Section Two as expected"); my_free(optstr); optstr = list2str(np_get_defaults("section_twice@./plugin.ini", "check_disk")); - ok(!strcmp(optstr, "--foo=bar --bar=foo"), "plugin.ini's section_twice defined twice in the file"); + ok(!strcmp(optstr, "--foo=bar --bar=foo"), + "plugin.ini's section_twice defined twice in the file"); my_free(optstr); optstr = list2str(np_get_defaults("tcp_long_lines@plugins.ini", "check_tcp")); - ok(!strcmp(optstr, "--escape --send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar " + ok(!strcmp(optstr, "--escape --send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo bar " "BAZ yadda yadda yadda Foo bar BAZ yadda yadda " - "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar " + "BAZ yadda yadda yadda Foo bar BAZ yadda " "yadda yadda Foo bar BAZ yadda yadda yadda Foo " - "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda yadda Foo bar BAZ yadda yadda " "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " - "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda --expect=Foo bar BAZ yadda yadda " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda --expect=Foo bar BAZ yadda yadda " "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " - "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo " "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " - "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar " + "BAZ yadda yadda yadda Foo bar BAZ yadda " "yadda yadda Foo bar BAZ yadda yadda yadda Foo " - "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda yadda Foo bar BAZ yadda yadda " "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " - "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo " "bar BAZ yadda yadda yadda --jail"), "Long options"); my_free(optstr); diff --git a/lib/tests/test_opts1.c b/lib/tests/test_opts1.c index 984183d3..99da5596 100644 --- a/lib/tests/test_opts1.c +++ b/lib/tests/test_opts1.c @@ -47,16 +47,18 @@ void my_free(int *argc, char **newargv, char **argv) { printf("'%s' ", newargv[i]); /* Stop freeing when we get to the start of the original array */ if (freeflag) { - if (newargv[i] == argv[1]) + if (newargv[i] == argv[1]) { freeflag = 0; - else + } else { free(newargv[i]); + } } } printf("\n"); /* Free only if it's a different array */ - if (newargv != argv) + if (newargv != argv) { free(newargv); + } *argc = 0; } #endif @@ -69,8 +71,9 @@ int array_diff(int i1, char **a1, int i2, char **a2) { return 0; } for (i = 0; i <= i1; i++) { - if (a1[i] == NULL && a2[i] == NULL) + if (a1[i] == NULL && a2[i] == NULL) { continue; + } if (a1[i] == NULL || a2[i] == NULL) { printf(" Argument # %i null in one array!\n", i); return 0; @@ -110,27 +113,36 @@ int main(int argc, char **argv) { { char *argv_test[] = {"prog_name", "--extra-opts=@./config-opts.ini", (char *)NULL}; argc_test = 2; - char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", (char *)NULL}; + char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", + (char *)NULL}; argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts using default section"); my_free(&argc_test, argv_new, argv_test); } { - char *argv_test[] = {"prog_name", "--extra-opts=sect1@./config-opts.ini", "--extra-opts", "sect2@./config-opts.ini", (char *)NULL}; + char *argv_test[] = {"prog_name", "--extra-opts=sect1@./config-opts.ini", "--extra-opts", + "sect2@./config-opts.ini", (char *)NULL}; argc_test = 4; - char *argv_known[] = {"prog_name", "--one=two", "--something else=oops", "--this=that", (char *)NULL}; + char *argv_known[] = {"prog_name", "--one=two", "--something else=oops", "--this=that", + (char *)NULL}; argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); ok(array_diff(argc_test, argv_new, 4, argv_known), "Only extra opts specified twice"); my_free(&argc_test, argv_new, argv_test); } { - char *argv_test[] = {"prog_name", "--arg1=val1", "--extra-opts=@./config-opts.ini", "--extra-opts", "sect1@./config-opts.ini", - "--arg2", (char *)NULL}; + char *argv_test[] = {"prog_name", + "--arg1=val1", + "--extra-opts=@./config-opts.ini", + "--extra-opts", + "sect1@./config-opts.ini", + "--arg2", + (char *)NULL}; argc_test = 6; - char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", "--blank", "--one=two", - "--arg1=val1", "--arg2", (char *)NULL}; + char *argv_known[] = {"prog_name", "--foo=Bar", "--this=Your Mother!", + "--blank", "--one=two", "--arg1=val1", + "--arg2", (char *)NULL}; argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); ok(array_diff(argc_test, argv_new, 7, argv_known), "twice extra opts using two sections"); my_free(&argc_test, argv_new, argv_test); diff --git a/lib/tests/test_opts2.c b/lib/tests/test_opts2.c index 23496617..d1b0aca3 100644 --- a/lib/tests/test_opts2.c +++ b/lib/tests/test_opts2.c @@ -30,16 +30,18 @@ void my_free(int *argc, char **newargv, char **argv) { printf("'%s' ", newargv[i]); /* Stop freeing when we get to the start of the original array */ if (freeflag) { - if (newargv[i] == argv[1]) + if (newargv[i] == argv[1]) { freeflag = 0; - else + } else { free(newargv[i]); + } } } printf("\n"); /* Free only if it's a different array */ - if (newargv != argv) + if (newargv != argv) { free(newargv); + } *argc = 0; } @@ -51,8 +53,9 @@ int array_diff(int i1, char **a1, int i2, char **a2) { return 0; } for (i = 0; i <= i1; i++) { - if (a1[i] == NULL && a2[i] == NULL) + if (a1[i] == NULL && a2[i] == NULL) { continue; + } if (a1[i] == NULL || a2[i] == NULL) { printf(" Argument # %i null in one array!\n", i); return 0; @@ -90,7 +93,8 @@ int main(int argc, char **argv) { } { - char *argv_test[] = {"prog_name", "arg1", "--extra-opts=section1", "--arg3", "val2", (char *)NULL}; + char *argv_test[] = {"prog_name", "arg1", "--extra-opts=section1", + "--arg3", "val2", (char *)NULL}; argc_test = 5; char *argv_known[] = {"prog_name", "--foobar=baz", "arg1", "--arg3", "val2", (char *)NULL}; argv_new = np_extra_opts(&argc_test, argv_test, "check_disk"); @@ -108,30 +112,39 @@ int main(int argc, char **argv) { } { - char *argv_test[] = {"check_tcp", "--extra-opts", "--extra-opts=tcp_long_lines", (char *)NULL}; + char *argv_test[] = {"check_tcp", "--extra-opts", "--extra-opts=tcp_long_lines", + (char *)NULL}; argc_test = 3; - char *argv_known[] = { - "check_tcp", - "--timeout=10", - "--escape", - "--send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " - "yadda Foo bar BAZ yadda " - "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " - "yadda Foo bar BAZ " - "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " - "yadda yadda Foo bar " - "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda", - "--expect=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " - "yadda Foo bar BAZ yadda " - "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " - "yadda Foo bar BAZ " - "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " - "yadda yadda Foo bar " - "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " - "yadda yadda yadda Foo " - "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda", - "--jail", - (char *)NULL}; + char *argv_known[] = {"check_tcp", + "--timeout=10", + "--escape", + "--send=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda " + "Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda " + "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda yadda Foo bar " + "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda", + "--expect=Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda " + "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda " + "yadda Foo bar BAZ " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ yadda " + "yadda yadda Foo bar " + "BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " + "yadda yadda yadda Foo " + "bar BAZ yadda yadda yadda Foo bar BAZ yadda yadda yadda Foo bar BAZ " + "yadda yadda yadda Foo bar BAZ yadda yadda yadda", + "--jail", + (char *)NULL}; argv_new = np_extra_opts(&argc_test, argv_test, "check_tcp"); ok(array_diff(argc_test, argv_new, 6, argv_known), "Long lines test"); my_free(&argc_test, argv_new, argv_test); diff --git a/lib/tests/test_tcp.c b/lib/tests/test_tcp.c index 1b3003e9..de3a2102 100644 --- a/lib/tests/test_tcp.c +++ b/lib/tests/test_tcp.c @@ -32,19 +32,28 @@ int main(void) { server_expect[1] = strdup("bb"); server_expect[2] = strdup("CC"); - ok(np_expect_match("AA bb CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_SUCCESS, + ok(np_expect_match("AA bb CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == + NP_MATCH_SUCCESS, "Test matching any string at the beginning (first expect string)"); - ok(np_expect_match("bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_SUCCESS, + ok(np_expect_match("bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == + NP_MATCH_SUCCESS, "Test matching any string at the beginning (second expect string)"); ok(np_expect_match("b", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_RETRY, "Test matching any string at the beginning (substring match)"); - ok(np_expect_match("XX bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_FAILURE, + ok(np_expect_match("XX bb AA CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == + NP_MATCH_FAILURE, "Test with strings not matching at the beginning"); - ok(np_expect_match("XX CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == NP_MATCH_FAILURE, "Test matching any string"); - ok(np_expect_match("XX", server_expect, server_expect_count, 0) == NP_MATCH_RETRY, "Test not matching any string"); - ok(np_expect_match("XX AA bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_SUCCESS, + ok(np_expect_match("XX CC XX", server_expect, server_expect_count, NP_MATCH_EXACT) == + NP_MATCH_FAILURE, + "Test matching any string"); + ok(np_expect_match("XX", server_expect, server_expect_count, 0) == NP_MATCH_RETRY, + "Test not matching any string"); + ok(np_expect_match("XX AA bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == + NP_MATCH_SUCCESS, "Test matching all strings"); - ok(np_expect_match("XX bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY, "Test not matching all strings"); + ok(np_expect_match("XX bb CC XX", server_expect, server_expect_count, NP_MATCH_ALL) == + NP_MATCH_RETRY, + "Test not matching all strings"); ok(np_expect_match("XX XX", server_expect, server_expect_count, NP_MATCH_ALL) == NP_MATCH_RETRY, "Test not matching any string (testing all)"); diff --git a/lib/utils_base.c b/lib/utils_base.c index e95eeaf0..69024bc9 100644 --- a/lib/utils_base.c +++ b/lib/utils_base.c @@ -254,7 +254,7 @@ bool check_range(double value, range *my_range) { yes = false; } - if (!my_range->end_infinity&& !my_range->start_infinity) { + if (!my_range->end_infinity && !my_range->start_infinity) { if ((my_range->start <= value) && (value <= my_range->end)) { return no; } @@ -268,7 +268,7 @@ bool check_range(double value, range *my_range) { return yes; } - if (my_range->start_infinity && !my_range->end_infinity ) { + if (my_range->start_infinity && !my_range->end_infinity) { if (value <= my_range->end) { return no; } diff --git a/lib/utils_cmd.c b/lib/utils_cmd.c index 9b222409..d1feaa33 100644 --- a/lib/utils_cmd.c +++ b/lib/utils_cmd.c @@ -71,7 +71,7 @@ extern char **environ; #endif #ifndef WIFEXITED -# define WIFEXITED(stat_val) (((stat_val)&255) == 0) +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif /* 4.3BSD Reno doesn't define SIG_ERR */ @@ -103,8 +103,9 @@ void cmd_init(void) { maxfd = MAXFD_LIMIT; } - if (!_cmd_pids) + if (!_cmd_pids) { _cmd_pids = calloc(maxfd, sizeof(pid_t)); + } } /* Start running a command, array style */ @@ -116,13 +117,15 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { int i = 0; - if (!_cmd_pids) + if (!_cmd_pids) { CMD_INIT; + } setenv("LC_ALL", "C", 1); - if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) + if (pipe(pfd) < 0 || pipe(pfderr) < 0 || (pid = fork()) < 0) { return -1; /* errno set by the failing function */ + } /* child runs exceve() and _exit. */ if (pid == 0) { @@ -147,9 +150,11 @@ static int _cmd_open(char *const *argv, int *pfd, int *pfderr) { * This is executed in a separate address space (pure child), * so we don't have to worry about async safety */ long maxfd = mp_open_max(); - for (i = 0; i < maxfd; i++) - if (_cmd_pids[i] > 0) + for (i = 0; i < maxfd; i++) { + if (_cmd_pids[i] > 0) { close(i); + } + } execve(argv[0], argv, environ); _exit(STATE_UNKNOWN); @@ -172,17 +177,21 @@ static int _cmd_close(int fd) { /* make sure the provided fd was opened */ long maxfd = mp_open_max(); - if (fd < 0 || fd > maxfd || !_cmd_pids || (pid = _cmd_pids[fd]) == 0) + if (fd < 0 || fd > maxfd || !_cmd_pids || (pid = _cmd_pids[fd]) == 0) { return -1; + } _cmd_pids[fd] = 0; - if (close(fd) == -1) + if (close(fd) == -1) { return -1; + } /* EINTR is ok (sort of), everything else is bad */ - while (waitpid(pid, &status, 0) < 0) - if (errno != EINTR) + while (waitpid(pid, &status, 0) < 0) { + if (errno != EINTR) { return -1; + } + } /* return child's termination status */ return (WIFEXITED(status)) ? WEXITSTATUS(status) : -1; @@ -212,15 +221,17 @@ static int _cmd_fetch_output(int fd, output *op, int flags) { /* some plugins may want to keep output unbroken, and some commands * will yield no output, so return here for those */ - if (flags & CMD_NO_ARRAYS || !op->buf || !op->buflen) + if (flags & CMD_NO_ARRAYS || !op->buf || !op->buflen) { return op->buflen; + } /* and some may want both */ if (flags & CMD_NO_ASSOC) { buf = malloc(op->buflen); memcpy(buf, op->buf, op->buflen); - } else + } else { buf = op->buf; + } op->line = NULL; op->lens = NULL; @@ -241,8 +252,9 @@ static int _cmd_fetch_output(int fd, output *op, int flags) { op->line[lineno] = &buf[i]; /* hop to next newline or end of buffer */ - while (buf[i] != '\n' && i < op->buflen) + while (buf[i] != '\n' && i < op->buflen) { i++; + } buf[i] = '\0'; /* calculate the string length using pointer difference */ @@ -262,30 +274,36 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { char *cmd = NULL; char *str = NULL; - if (cmdstring == NULL) + if (cmdstring == NULL) { return -1; + } /* initialize the structs */ - if (out) + if (out) { memset(out, 0, sizeof(output)); - if (err) + } + if (err) { memset(err, 0, sizeof(output)); + } /* make copy of command string so strtok() doesn't silently modify it */ /* (the calling program may want to access it later) */ cmdlen = strlen(cmdstring); - if ((cmd = malloc(cmdlen + 1)) == NULL) + if ((cmd = malloc(cmdlen + 1)) == NULL) { return -1; + } memcpy(cmd, cmdstring, cmdlen); cmd[cmdlen] = '\0'; /* This is not a shell, so we don't handle "???" */ - if (strstr(cmdstring, "\"")) + if (strstr(cmdstring, "\"")) { return -1; + } /* allow single quotes, but only if non-whitesapce doesn't occur on both sides */ - if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) + if (strstr(cmdstring, " ' ") || strstr(cmdstring, "'''")) { return -1; + } /* each arg must be whitespace-separated, so args can be a maximum * of (len / 2) + 1. We add 1 extra to the mix for NULL termination */ @@ -304,8 +322,9 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { if (strstr(str, "'") == str) { /* handle SIMPLE quoted strings */ str++; - if (!strstr(str, "'")) + if (!strstr(str, "'")) { return -1; /* balanced? */ + } cmd = 1 + strstr(str, "'"); str[strcspn(str, "'")] = 0; } else { @@ -317,8 +336,9 @@ int cmd_run(const char *cmdstring, output *out, output *err, int flags) { } } - if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) + if (cmd && strlen(cmd) == strspn(cmd, " \t\r\n")) { cmd = NULL; + } argv[i++] = str; } @@ -330,50 +350,61 @@ int cmd_run_array(char *const *argv, output *out, output *err, int flags) { int fd, pfd_out[2], pfd_err[2]; /* initialize the structs */ - if (out) + if (out) { memset(out, 0, sizeof(output)); - if (err) + } + if (err) { memset(err, 0, sizeof(output)); + } - if ((fd = _cmd_open(argv, pfd_out, pfd_err)) == -1) + if ((fd = _cmd_open(argv, pfd_out, pfd_err)) == -1) { die(STATE_UNKNOWN, _("Could not open pipe: %s\n"), argv[0]); + } - if (out) + if (out) { out->lines = _cmd_fetch_output(pfd_out[0], out, flags); - if (err) + } + if (err) { err->lines = _cmd_fetch_output(pfd_err[0], err, flags); + } return _cmd_close(fd); } int cmd_file_read(const char *filename, output *out, int flags) { int fd; - if (out) + if (out) { memset(out, 0, sizeof(output)); + } if ((fd = open(filename, O_RDONLY)) == -1) { die(STATE_UNKNOWN, _("Error opening %s: %s"), filename, strerror(errno)); } - if (out) + if (out) { out->lines = _cmd_fetch_output(fd, out, flags); + } - if (close(fd) == -1) + if (close(fd) == -1) { die(STATE_UNKNOWN, _("Error closing %s: %s"), filename, strerror(errno)); + } return 0; } void timeout_alarm_handler(int signo) { if (signo == SIGALRM) { - printf(_("%s - Plugin timed out after %d seconds\n"), state_text(timeout_state), timeout_interval); + printf(_("%s - Plugin timed out after %d seconds\n"), state_text(timeout_state), + timeout_interval); long maxfd = mp_open_max(); - if (_cmd_pids) + if (_cmd_pids) { for (long int i = 0; i < maxfd; i++) { - if (_cmd_pids[i] != 0) + if (_cmd_pids[i] != 0) { kill(_cmd_pids[i], SIGKILL); + } } + } exit(timeout_state); } diff --git a/lib/utils_tcp.c b/lib/utils_tcp.c index daae1d54..1482458b 100644 --- a/lib/utils_tcp.c +++ b/lib/utils_tcp.c @@ -29,18 +29,21 @@ #include "common.h" #include "utils_tcp.h" -#define VERBOSE(message) \ - do { \ - if (flags & NP_MATCH_VERBOSE) \ - puts(message); \ +#define VERBOSE(message) \ + do { \ + if (flags & NP_MATCH_VERBOSE) \ + puts(message); \ } while (0) -enum np_match_result np_expect_match(char *status, char **server_expect, int expect_count, int flags) { +enum np_match_result np_expect_match(char *status, char **server_expect, int expect_count, + int flags) { int i, match = 0, partial = 0; for (i = 0; i < expect_count; i++) { - if (flags & NP_MATCH_VERBOSE) - printf("looking for [%s] %s [%s]\n", server_expect[i], (flags & NP_MATCH_EXACT) ? "in beginning of" : "anywhere in", status); + if (flags & NP_MATCH_VERBOSE) { + printf("looking for [%s] %s [%s]\n", server_expect[i], + (flags & NP_MATCH_EXACT) ? "in beginning of" : "anywhere in", status); + } if (flags & NP_MATCH_EXACT) { if (strncmp(status, server_expect[i], strlen(server_expect[i])) == 0) { @@ -60,10 +63,12 @@ enum np_match_result np_expect_match(char *status, char **server_expect, int exp VERBOSE("couldn't find it"); } - if ((flags & NP_MATCH_ALL && match == expect_count) || (!(flags & NP_MATCH_ALL) && match >= 1)) + if ((flags & NP_MATCH_ALL && match == expect_count) || + (!(flags & NP_MATCH_ALL) && match >= 1)) { return NP_MATCH_SUCCESS; - else if (partial > 0 || !(flags & NP_MATCH_EXACT)) + } else if (partial > 0 || !(flags & NP_MATCH_EXACT)) { return NP_MATCH_RETRY; - else + } else { return NP_MATCH_FAILURE; + } } diff --git a/lib/utils_tcp.h b/lib/utils_tcp.h index a7d83c59..e5cdbb82 100644 --- a/lib/utils_tcp.h +++ b/lib/utils_tcp.h @@ -17,4 +17,5 @@ enum np_match_result { NP_MATCH_RETRY }; -enum np_match_result np_expect_match(char *status, char **server_expect, int server_expect_count, int flags); +enum np_match_result np_expect_match(char *status, char **server_expect, int server_expect_count, + int flags); diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c index daed9cb0..9a96547f 100644 --- a/plugins-root/check_dhcp.c +++ b/plugins-root/check_dhcp.c @@ -127,17 +127,17 @@ static long mac_addr_dlpi(const char *, int, u_char *); #define MAX_DHCP_OPTIONS_LENGTH 312 typedef struct dhcp_packet_struct { - uint8_t op; /* packet type */ - uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ - uint8_t hlen; /* length of hardware address (of this machine) */ - uint8_t hops; /* hops */ - uint32_t xid; /* random transaction id number - chosen by this machine */ - uint16_t secs; /* seconds used in timing */ - uint16_t flags; /* flags */ - struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ - struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ - struct in_addr siaddr; /* IP address of next server */ - struct in_addr giaddr; /* IP address of DHCP relay */ + uint8_t op; /* packet type */ + uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ + uint8_t hlen; /* length of hardware address (of this machine) */ + uint8_t hops; /* hops */ + uint32_t xid; /* random transaction id number - chosen by this machine */ + uint16_t secs; /* seconds used in timing */ + uint16_t flags; /* flags */ + struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ + struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ + struct in_addr siaddr; /* IP address of next server */ + struct in_addr giaddr; /* IP address of DHCP relay */ unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */ char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */ char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */ @@ -199,7 +199,8 @@ static void print_help(void); static void resolve_host(const char * /*in*/, struct in_addr * /*out*/); static unsigned char *mac_aton(const char * /*string*/); static void print_hardware_address(const unsigned char * /*address*/); -static int get_hardware_address(int /*sock*/, char * /*interface_name*/, unsigned char *client_hardware_address); +static int get_hardware_address(int /*sock*/, char * /*interface_name*/, + unsigned char *client_hardware_address); typedef struct get_ip_address_wrapper { int error; @@ -211,32 +212,40 @@ typedef struct send_dhcp_discover_wrapper { int error; uint32_t packet_xid; } send_dhcp_discover_wrapper; -static send_dhcp_discover_wrapper send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address, - bool request_specific_address, struct in_addr my_ip, - unsigned char *client_hardware_address); +static send_dhcp_discover_wrapper +send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip, + struct in_addr requested_address, bool request_specific_address, + struct in_addr my_ip, unsigned char *client_hardware_address); typedef struct get_dhcp_offer_wrapper { int error; int valid_responses; dhcp_offer *dhcp_offer_list; } get_dhcp_offer_wrapper; -static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list, +static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout, + uint32_t packet_xid, dhcp_offer *dhcp_offer_list, const unsigned char *client_hardware_address); -static mp_subcheck get_results(bool exclusive, int requested_servers, struct in_addr requested_address, bool request_specific_address, - requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list); +static mp_subcheck get_results(bool exclusive, int requested_servers, + struct in_addr requested_address, bool request_specific_address, + requested_server *requested_server_list, int valid_responses, + dhcp_offer *dhcp_offer_list); typedef struct add_dhcp_offer_wrapper { int error; dhcp_offer *dhcp_offer_list; } add_dhcp_offer_wrapper; -static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/, dhcp_packet * /*offer_packet*/, dhcp_offer *dhcp_offer_list); +static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/, + dhcp_packet * /*offer_packet*/, + dhcp_offer *dhcp_offer_list); static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list); static int free_requested_server_list(requested_server *requested_server_list); static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name); static int close_dhcp_socket(int /*sock*/); -static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, struct sockaddr_in * /*dest*/); -static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, int /*timeout*/, struct sockaddr_in * /*address*/); +static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, + struct sockaddr_in * /*dest*/); +static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, + int /*timeout*/, struct sockaddr_in * /*address*/); int main(int argc, char **argv) { setlocale(LC_ALL, ""); @@ -271,7 +280,8 @@ int main(int argc, char **argv) { struct in_addr my_ip = {0}; if (config.unicast_mode) { /* get IP address of client machine */ - get_ip_address_wrapper tmp_get_ip = get_ip_address(dhcp_socket, config.network_interface_name); + get_ip_address_wrapper tmp_get_ip = + get_ip_address(dhcp_socket, config.network_interface_name); if (tmp_get_ip.error == OK) { my_ip = tmp_get_ip.my_ip; } else { @@ -281,8 +291,9 @@ int main(int argc, char **argv) { } /* send DHCPDISCOVER packet */ - send_dhcp_discover_wrapper disco_res = send_dhcp_discover(dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address, - config.request_specific_address, my_ip, client_hardware_address); + send_dhcp_discover_wrapper disco_res = send_dhcp_discover( + dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address, + config.request_specific_address, my_ip, client_hardware_address); if (disco_res.error != OK) { // DO something? @@ -290,8 +301,8 @@ int main(int argc, char **argv) { } /* wait for a DHCPOFFER packet */ - get_dhcp_offer_wrapper offer_res = - get_dhcp_offer(dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address); + get_dhcp_offer_wrapper offer_res = get_dhcp_offer( + dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address); int valid_responses = 0; dhcp_offer *dhcp_offer_list = NULL; @@ -308,8 +319,10 @@ int main(int argc, char **argv) { mp_check overall = mp_check_init(); /* determine state/plugin output to return */ - mp_subcheck sc_res = get_results(config.exclusive_mode, config.num_of_requested_servers, config.requested_address, - config.request_specific_address, config.requested_server_list, valid_responses, dhcp_offer_list); + mp_subcheck sc_res = + get_results(config.exclusive_mode, config.num_of_requested_servers, + config.requested_address, config.request_specific_address, + config.requested_server_list, valid_responses, dhcp_offer_list); mp_add_subcheck_to_check(&overall, sc_res); /* free allocated memory */ free_dhcp_offer_list(dhcp_offer_list); @@ -357,17 +370,20 @@ int get_hardware_address(int sock, char *interface_name, unsigned char *client_h } if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { - printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), interface_name, strerror(errno)); + printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), + interface_name, strerror(errno)); exit(STATE_UNKNOWN); } if ((buf = malloc(len)) == NULL) { - printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), interface_name, strerror(errno)); + printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), + interface_name, strerror(errno)); exit(4); } if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { - printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), interface_name, strerror(errno)); + printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), + interface_name, strerror(errno)); exit(STATE_UNKNOWN); } @@ -398,12 +414,16 @@ int get_hardware_address(int sock, char *interface_name, unsigned char *client_h unit = atoi(p); strncat(dev, interface_name, 6); } else { - printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n"), interface_name); + printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg " + "lnc0.\n"), + interface_name); exit(STATE_UNKNOWN); } stat = mac_addr_dlpi(dev, unit, client_hardware_address); if (stat != 0) { - printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); + printf( + _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), + dev, unit); exit(STATE_UNKNOWN); } @@ -415,7 +435,9 @@ int get_hardware_address(int sock, char *interface_name, unsigned char *client_h stat = mac_addr_dlpi(dev, unit, client_hardware_address); if (stat != 0) { - printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); + printf( + _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), + dev, unit); exit(STATE_UNKNOWN); } /* Kompf 2000-2003 */ @@ -463,8 +485,10 @@ get_ip_address_wrapper get_ip_address(int sock, char *interface_name) { } /* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ -static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address, - bool request_specific_address, struct in_addr my_ip, +static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip, + struct in_addr requested_address, + bool request_specific_address, + struct in_addr my_ip, unsigned char *client_hardware_address) { dhcp_packet discover_packet = {0}; /* boot request flag (backward compatible with BOOTP servers) */ @@ -506,8 +530,9 @@ static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, str unsigned short opts = 4; /* DHCP message type is embedded in options field */ - discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ - discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ + discover_packet.options[opts++] = + DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ + discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ discover_packet.options[opts++] = DHCPDISCOVER; /* the IP address we're requesting */ @@ -535,8 +560,10 @@ static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, str }; if (verbose) { - printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); - printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid), ntohl(discover_packet.xid)); + printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), + ntohs(sockaddr_broadcast.sin_port)); + printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid), + ntohl(discover_packet.xid)); printf("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr)); printf("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr)); printf("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr)); @@ -554,7 +581,8 @@ static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, str } /* waits for a DHCPOFFER message from one or more DHCP servers */ -get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list, +get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid, + dhcp_offer *dhcp_offer_list, const unsigned char *client_hardware_address) { time_t start_time; time(&start_time); @@ -578,7 +606,8 @@ get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t dhcp_packet offer_packet = {0}; result = OK; - result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); + result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, + &source); if (result != OK) { if (verbose) { @@ -607,8 +636,9 @@ get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t /* check packet xid to see if its the same as the one we used in the discover packet */ if (ntohl(offer_packet.xid) != packet_xid) { if (verbose) { - printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), - packet_xid); + printf( + _("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), + ntohl(offer_packet.xid), packet_xid); } continue; @@ -648,7 +678,8 @@ get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); } - add_dhcp_offer_wrapper add_res = add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list); + add_dhcp_offer_wrapper add_res = + add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list); if (add_res.error != OK) { // TODO } else { @@ -673,7 +704,8 @@ get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t /* sends a DHCP packet */ int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { - int result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest)); + int result = + sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest)); if (verbose) { printf(_("send_dhcp_packet result: %d\n"), result); @@ -687,7 +719,8 @@ int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in } /* receives a DHCP packet */ -int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { +int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, + struct sockaddr_in *address) { /* wait for data to arrive (up time timeout) */ struct timeval timeout_val = { .tv_sec = timeout, @@ -711,7 +744,8 @@ int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, st struct sockaddr_in source_address = {0}; socklen_t address_size = sizeof(source_address); - int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); + int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, + (struct sockaddr *)&source_address, &address_size); if (verbose) { printf("recv_result: %d\n", recv_result); } @@ -775,7 +809,8 @@ int create_dhcp_socket(bool unicast, char *network_interface_name) { strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); interface.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0'; if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) { - printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"), network_interface_name); + printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"), + network_interface_name); exit(STATE_UNKNOWN); } @@ -786,7 +821,8 @@ int create_dhcp_socket(bool unicast, char *network_interface_name) { /* bind the socket */ if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) { - printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"), DHCP_CLIENT_PORT); + printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"), + DHCP_CLIENT_PORT); exit(STATE_UNKNOWN); } @@ -800,7 +836,8 @@ int close_dhcp_socket(int sock) { } /* adds a requested server address to list in memory */ -int add_requested_server(struct in_addr server_address, int *requested_servers, requested_server **requested_server_list) { +int add_requested_server(struct in_addr server_address, int *requested_servers, + requested_server **requested_server_list) { requested_server *new_server = (requested_server *)malloc(sizeof(requested_server)); if (new_server == NULL) { return ERROR; @@ -822,7 +859,8 @@ int add_requested_server(struct in_addr server_address, int *requested_servers, } /* adds a DHCP OFFER to list in memory */ -add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet, dhcp_offer *dhcp_offer_list) { +add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet, + dhcp_offer *dhcp_offer_list) { if (offer_packet == NULL) { add_dhcp_offer_wrapper tmp = { .error = ERROR, @@ -859,15 +897,18 @@ add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_ dhcp_lease_time = ntohl(dhcp_lease_time); break; case DHCP_OPTION_RENEWAL_TIME: - memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_renewal_time)); + memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx], + sizeof(dhcp_renewal_time)); dhcp_renewal_time = ntohl(dhcp_renewal_time); break; case DHCP_OPTION_REBINDING_TIME: - memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_rebinding_time)); + memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx], + sizeof(dhcp_rebinding_time)); dhcp_rebinding_time = ntohl(dhcp_rebinding_time); break; case DHCP_OPTION_SERVER_IDENTIFIER: - memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx], sizeof(serv_ident.s_addr)); + memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx], + sizeof(serv_ident.s_addr)); break; } @@ -955,7 +996,8 @@ int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) { /* frees memory allocated to requested server list */ int free_requested_server_list(requested_server *requested_server_list) { requested_server *next_server; - for (requested_server *this_server = requested_server_list; this_server != NULL; this_server = next_server) { + for (requested_server *this_server = requested_server_list; this_server != NULL; + this_server = next_server) { next_server = this_server->next; free(this_server); } @@ -964,8 +1006,10 @@ int free_requested_server_list(requested_server *requested_server_list) { } /* gets state and plugin output to return */ -mp_subcheck get_results(bool exclusive, const int requested_servers, const struct in_addr requested_address, bool request_specific_address, - requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list) { +mp_subcheck get_results(bool exclusive, const int requested_servers, + const struct in_addr requested_address, bool request_specific_address, + requested_server *requested_server_list, int valid_responses, + dhcp_offer *dhcp_offer_list) { mp_subcheck sc_dhcp_results = mp_subcheck_init(); sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK); @@ -995,22 +1039,28 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc /* checks responses from requested servers */ int requested_responses = 0; if (requested_servers > 0) { - for (requested_server *temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { - for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + for (requested_server *temp_server = requested_server_list; temp_server != NULL; + temp_server = temp_server->next) { + for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; + temp_offer = temp_offer->next) { /* get max lease time we were offered */ - if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { + if (temp_offer->lease_time > max_lease_time || + temp_offer->lease_time == DHCP_INFINITE_TIME) { max_lease_time = temp_offer->lease_time; } /* see if we got the address we requested */ - if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) { + if (!memcmp(&requested_address, &temp_offer->offered_address, + sizeof(requested_address))) { received_requested_address = true; } /* see if the servers we wanted a response from, talked to us or not */ - if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { + if (!memcmp(&temp_offer->server_address, &temp_server->server_address, + sizeof(temp_server->server_address))) { if (verbose) { - printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); + printf(_("DHCP Server Match: Offerer=%s"), + inet_ntoa(temp_offer->server_address)); printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); if (temp_server->answered) { printf(_(" (duplicate)")); @@ -1028,7 +1078,8 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc } /* exclusive mode: check for undesired offers */ - for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; + temp_offer = temp_offer->next) { if (!temp_offer->desired) { undesired_offer = temp_offer; /* Checks only for the first undesired offer */ break; /* no further checks needed */ @@ -1036,7 +1087,8 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc } mp_subcheck sc_rqust_srvs = mp_subcheck_init(); - xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded", requested_responses, requested_servers); + xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded", + requested_responses, requested_servers); if (requested_responses == requested_servers) { sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK); @@ -1053,14 +1105,17 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc } else { /* else check and see if we got our requested address from any server */ - for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { + for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; + temp_offer = temp_offer->next) { /* get max lease time we were offered */ - if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { + if (temp_offer->lease_time > max_lease_time || + temp_offer->lease_time == DHCP_INFINITE_TIME) { max_lease_time = temp_offer->lease_time; } /* see if we got the address we requested */ - if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) { + if (!memcmp(&requested_address, &temp_offer->offered_address, + sizeof(requested_address))) { received_requested_address = true; } } @@ -1069,7 +1124,8 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc if (max_lease_time == DHCP_INFINITE_TIME) { xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output); } else { - xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds", sc_dhcp_results.output, max_lease_time); + xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds", + sc_dhcp_results.output, max_lease_time); } if (exclusive) { @@ -1083,8 +1139,8 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc // Get the addresses for printout // 1.address of the sending server char server_address[INET_ADDRSTRLEN]; - const char *server_address_transformed = - inet_ntop(AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address)); + const char *server_address_transformed = inet_ntop( + AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address)); if (server_address != server_address_transformed) { die(STATE_UNKNOWN, "inet_ntop failed"); @@ -1093,13 +1149,15 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc // 2.address offered char offered_address[INET_ADDRSTRLEN]; const char *offered_address_transformed = - inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address, sizeof(offered_address)); + inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address, + sizeof(offered_address)); if (offered_address != offered_address_transformed) { die(STATE_UNKNOWN, "inet_ntop failed"); } - xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s", server_address, offered_address); + xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s", + server_address, offered_address); } else { sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK); xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected"); @@ -1112,10 +1170,12 @@ mp_subcheck get_results(bool exclusive, const int requested_servers, const struc if (received_requested_address) { sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK); - xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered", inet_ntoa(requested_address)); + xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered", + inet_ntoa(requested_address)); } else { sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING); - xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered", inet_ntoa(requested_address)); + xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered", + inet_ntoa(requested_address)); } mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr); @@ -1138,18 +1198,19 @@ process_arguments_wrapper process_arguments(int argc, char **argv) { }; int option_index = 0; - static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, - {"requestedip", required_argument, 0, 'r'}, - {"timeout", required_argument, 0, 't'}, - {"interface", required_argument, 0, 'i'}, - {"mac", required_argument, 0, 'm'}, - {"unicast", no_argument, 0, 'u'}, - {"exclusive", no_argument, 0, 'x'}, - {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, - {"output-format", required_argument, 0, output_format_index}, - {0, 0, 0, 0}}; + static struct option long_options[] = { + {"serverip", required_argument, 0, 's'}, + {"requestedip", required_argument, 0, 'r'}, + {"timeout", required_argument, 0, 't'}, + {"interface", required_argument, 0, 'i'}, + {"mac", required_argument, 0, 'm'}, + {"unicast", no_argument, 0, 'u'}, + {"exclusive", no_argument, 0, 'x'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"output-format", required_argument, 0, output_format_index}, + {0, 0, 0, 0}}; check_dhcp_config config = check_dhcp_config_init(); int option_char = 0; @@ -1163,7 +1224,8 @@ process_arguments_wrapper process_arguments(int argc, char **argv) { switch (option_char) { case 's': /* DHCP server address */ resolve_host(optarg, &config.dhcp_ip); - add_requested_server(config.dhcp_ip, &config.num_of_requested_servers, &config.requested_server_list); + add_requested_server(config.dhcp_ip, &config.num_of_requested_servers, + &config.requested_server_list); break; case 'r': /* address we are requested from DHCP servers */ @@ -1187,7 +1249,8 @@ process_arguments_wrapper process_arguments(int argc, char **argv) { break; case 'i': /* interface name */ - strncpy(config.network_interface_name, optarg, sizeof(config.network_interface_name) - 1); + strncpy(config.network_interface_name, optarg, + sizeof(config.network_interface_name) - 1); config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0'; break; @@ -1289,7 +1352,8 @@ static int put_ctrl(int fd, int len, int pri) { ctl.len = len; if (putmsg(fd, &ctl, 0, pri) < 0) { - printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), strerror(errno)); + printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), + strerror(errno)); exit(STATE_UNKNOWN); } @@ -1302,7 +1366,8 @@ static int put_both(int fd, int clen, int dlen, int pri) { ctl.len = clen; dat.len = dlen; if (putmsg(fd, &ctl, &dat, pri) < 0) { - printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), strerror(errno)); + printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), + strerror(errno)); exit(STATE_UNKNOWN); } @@ -1314,7 +1379,8 @@ static int dl_open(const char *dev, int unit, int *fd) { dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area; if ((*fd = open(dev, O_RDWR)) == -1) { - printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), dev, strerror(errno)); + printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), + dev, strerror(errno)); exit(STATE_UNKNOWN); } attach_req->dl_primitive = DL_ATTACH_REQ; @@ -1338,7 +1404,8 @@ static int dl_bind(int fd, int sap, u_char *addr) { put_ctrl(fd, sizeof(dl_bind_req_t), 0); get_msg(fd); if (GOT_ERR == check_ctrl(DL_BIND_ACK)) { - printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), strerror(errno)); + printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), + strerror(errno)); exit(STATE_UNKNOWN); } bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length); @@ -1455,7 +1522,8 @@ void print_help(void) { printf(" %s\n", "-u, --unicast"); printf(" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s")); printf(" %s\n", "-x, --exclusive"); - printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); + printf(" %s\n", + _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); printf(UT_SUPPORT); } diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h index c348bef5..be388d0a 100644 --- a/plugins-root/check_icmp.d/config.h +++ b/plugins-root/check_icmp.d/config.h @@ -97,7 +97,7 @@ typedef struct icmp_ping_data { /* 80 msec packet interval by default */ // DEPRECATED, remove when removing the option -#define DEFAULT_PKT_INTERVAL 80000 +#define DEFAULT_PKT_INTERVAL 80000 #define DEFAULT_TARGET_INTERVAL 0 diff --git a/plugins-root/pst3.c b/plugins-root/pst3.c index 1f69f3a6..1bfe3d35 100644 --- a/plugins-root/pst3.c +++ b/plugins-root/pst3.c @@ -1,45 +1,45 @@ /***************************************************************************** -* -* pst3 -* -* License: GPL -* Copyright (c) 2008 Monitoring Plugins Development Team -* -* Description: -* -* This file contains the pst3 executable. This is a replacement ps command -* for Solaris to get output which provides a long argument listing, which -* is not possible with the standard ps command (due to truncation). /usr/ucb/ps -* also has issues where some fields run into each other. -* -* This executable works by reading process address structures, so needs -* to be executed as root -* -* Originally written by R.W.Ingraham -* Rewritten by Duncan Ferguson (Altinity Ltd, June 2008) -* The rewrite was necessary as /dev/kmem is not available within -* non-global zones on Solaris 10 -* -* Details for rewrite came from -* source of /usr/ucb/ps on Solaris: -* http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff -* usenet group posting -* http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F -* -* 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 . -* -*****************************************************************************/ + * + * pst3 + * + * License: GPL + * Copyright (c) 2008 Monitoring Plugins Development Team + * + * Description: + * + * This file contains the pst3 executable. This is a replacement ps command + * for Solaris to get output which provides a long argument listing, which + * is not possible with the standard ps command (due to truncation). /usr/ucb/ps + * also has issues where some fields run into each other. + * + * This executable works by reading process address structures, so needs + * to be executed as root + * + * Originally written by R.W.Ingraham + * Rewritten by Duncan Ferguson (Altinity Ltd, June 2008) + * The rewrite was necessary as /dev/kmem is not available within + * non-global zones on Solaris 10 + * + * Details for rewrite came from + * source of /usr/ucb/ps on Solaris: + * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff + * usenet group posting + * http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F + * + * 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 #include @@ -55,14 +55,14 @@ * Constants */ -#define PROC_DIR "/proc" -#define ARGS 30 +#define PROC_DIR "/proc" +#define ARGS 30 /* * Globals */ -static char * szProg; +static char *szProg; /* * Prototypes @@ -71,192 +71,179 @@ void usage(); /*----------------------------------------------------------------------------*/ -int main (int argc, char **argv) -{ - DIR *procdir; - struct dirent *proc; - char ps_name[ARGS]; - char as_name[ARGS]; - psinfo_t psinfo; - - /* Set our program name global */ - if ((szProg = strrchr(argv[0], '/')) != NULL) - szProg++; - else - szProg = argv[0]; - - /* if given any parameters, print out help */ - if(argc > 1) { - (void)usage(); - exit(1); - } - - /* Make sure that our euid is root */ - if (geteuid() != 0) - { - fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg); - exit(1); - } - - if ((procdir = opendir(PROC_DIR)) == NULL) { - fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR); - exit(1); - } - - /* Display column headings */ - printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", - 'S', - "UID", - "PID", - "PPID", - "VSZ", - "RSS", - "%CPU", - "COMMAND", - "ARGS" - ); - - /* Zip through all of the process entries */ - while((proc = readdir(procdir))) { - int ps_fd; - int as_fd; - off_t argoff; - int i; - char *args; - char *procname; - char *ptr; - int argslen; - uintptr_t args_addr;; - uintptr_t *args_vecs;; - int args_count; - - if(proc->d_name[0] == '.') - continue; - - sprintf(ps_name,"%s/%s/%s",PROC_DIR,proc->d_name,"psinfo"); - sprintf(as_name,"%s/%s/%s",PROC_DIR,proc->d_name,"as"); -try_again: - if((ps_fd = open(ps_name, O_RDONLY)) == -1) - continue; - - if((as_fd = open(as_name, O_RDONLY)) == -1) { - close(ps_fd); - continue; - } - - if(read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { - int err = errno; - close(ps_fd); - close(as_fd); - if(err == EAGAIN) goto try_again; - if(err != ENOENT) - fprintf(stderr, "%s: read() on %s: %s\n", szProg, - ps_name, strerror(err)); - continue; - } - close(ps_fd); - - /* system process, ignore since the previous version did */ - if( - psinfo.pr_nlwp == 0 || - strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0 - ) { - continue; - } - - /* get the procname to match previous versions */ - procname = strdup(psinfo.pr_psargs); - if((ptr = strchr(procname, ' ')) != NULL) - *ptr = '\0'; - if((ptr = strrchr(procname, '/')) != NULL) - ptr++; - else - ptr = procname; - - /* - * print out what we currently know - */ - printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", - psinfo.pr_lwp.pr_sname, - psinfo.pr_euid, - psinfo.pr_pid, - psinfo.pr_ppid, - psinfo.pr_size, - psinfo.pr_rssize, - ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), - ptr - ); - free(procname); - - /* - * and now for the command line stuff - */ - - args_addr = psinfo.pr_argv; - args_count = psinfo.pr_argc; - args_vecs = malloc(args_count * sizeof(uintptr_t)); - - if(psinfo.pr_dmodel == PR_MODEL_NATIVE) { - /* this process matches target process */ - pread(as_fd,args_vecs, args_count * sizeof(uintptr_t), - args_addr); - } else { - /* this process is 64bit, target process is 32 bit*/ - caddr32_t *args_vecs32 = (caddr32_t *)args_vecs; - pread(as_fd,args_vecs32,args_count * sizeof(caddr32_t), - args_addr); - for (i=args_count-1;i>=0;--i) - args_vecs[i]=args_vecs32[i]; - } - - /* - * now read in the args - if what we read in fills buffer - * resize buffer and reread that bit again - */ - argslen=ARGS; - args=malloc(argslen+1); - for(i=0;i 1) { + (void)usage(); + exit(1); + } + + /* Make sure that our euid is root */ + if (geteuid() != 0) { + fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg); + exit(1); + } + + if ((procdir = opendir(PROC_DIR)) == NULL) { + fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR); + exit(1); + } + + /* Display column headings */ + printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", 'S', "UID", "PID", "PPID", "VSZ", "RSS", "%CPU", + "COMMAND", "ARGS"); + + /* Zip through all of the process entries */ + while ((proc = readdir(procdir))) { + int ps_fd; + int as_fd; + off_t argoff; + int i; + char *args; + char *procname; + char *ptr; + int argslen; + uintptr_t args_addr; + ; + uintptr_t *args_vecs; + ; + int args_count; + + if (proc->d_name[0] == '.') { + continue; + } + + sprintf(ps_name, "%s/%s/%s", PROC_DIR, proc->d_name, "psinfo"); + sprintf(as_name, "%s/%s/%s", PROC_DIR, proc->d_name, "as"); + try_again: + if ((ps_fd = open(ps_name, O_RDONLY)) == -1) { + continue; + } + + if ((as_fd = open(as_name, O_RDONLY)) == -1) { + close(ps_fd); + continue; + } + + if (read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { + int err = errno; + close(ps_fd); + close(as_fd); + if (err == EAGAIN) { + goto try_again; + } + if (err != ENOENT) { + fprintf(stderr, "%s: read() on %s: %s\n", szProg, ps_name, strerror(err)); + } + continue; + } + close(ps_fd); + + /* system process, ignore since the previous version did */ + if (psinfo.pr_nlwp == 0 || strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0) { + continue; + } + + /* get the procname to match previous versions */ + procname = strdup(psinfo.pr_psargs); + if ((ptr = strchr(procname, ' ')) != NULL) { + *ptr = '\0'; + } + if ((ptr = strrchr(procname, '/')) != NULL) { + ptr++; + } else { + ptr = procname; + } + + /* + * print out what we currently know + */ + printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", psinfo.pr_lwp.pr_sname, psinfo.pr_euid, + psinfo.pr_pid, psinfo.pr_ppid, psinfo.pr_size, psinfo.pr_rssize, + ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), ptr); + free(procname); + + /* + * and now for the command line stuff + */ + + args_addr = psinfo.pr_argv; + args_count = psinfo.pr_argc; + args_vecs = malloc(args_count * sizeof(uintptr_t)); + + if (psinfo.pr_dmodel == PR_MODEL_NATIVE) { + /* this process matches target process */ + pread(as_fd, args_vecs, args_count * sizeof(uintptr_t), args_addr); + } else { + /* this process is 64bit, target process is 32 bit*/ + caddr32_t *args_vecs32 = (caddr32_t *)args_vecs; + pread(as_fd, args_vecs32, args_count * sizeof(caddr32_t), args_addr); + for (i = args_count - 1; i >= 0; --i) { + args_vecs[i] = args_vecs32[i]; + } + } + + /* + * now read in the args - if what we read in fills buffer + * resize buffer and reread that bit again + */ + argslen = ARGS; + args = malloc(argslen + 1); + for (i = 0; i < args_count; i++) { + memset(args, '\0', argslen + 1); + if (pread(as_fd, args, argslen, args_vecs[i]) <= 0) { + break; + } + args[argslen] = '\0'; + if (strlen(args) == argslen) { + argslen += ARGS; + args = realloc(args, argslen + 1); + i--; + continue; + } + printf(" %s", args); + } + free(args_vecs); + free(args); + close(as_fd); + printf("\n"); + } + + (void)closedir(procdir); + + return (0); } /*----------------------------------------------------------------------------*/ void usage() { - printf("%s: Help output\n\n", szProg); - printf("If this program is given any arguments, this help is displayed.\n"); - printf("This command is used to print out the full command line for all\n"); - printf("running processes because /usr/bin/ps is limited to 80 chars and\n"); - printf("/usr/ucb/ps can merge columns together.\n\n"); - printf("Columns are:\n"); - printf("\tS - State of process - see 'ps' man page\n"); - printf("\tUID - UID of the process owner\n"); - printf("\tPID - PID of the process\n"); - printf("\tPPID - PID of the parent process\n"); - printf("\tVSZ - Virtual memory usage (kilobytes)\n"); - printf("\tRSS - Real memory usage (kilobytes)\n"); - printf("\t%%CPU - CPU usage\n"); - printf("\tCOMMAND - Command being run\n"); - printf("\tARGS - Full command line with arguments\n"); - return; + printf("%s: Help output\n\n", szProg); + printf("If this program is given any arguments, this help is displayed.\n"); + printf("This command is used to print out the full command line for all\n"); + printf("running processes because /usr/bin/ps is limited to 80 chars and\n"); + printf("/usr/ucb/ps can merge columns together.\n\n"); + printf("Columns are:\n"); + printf("\tS - State of process - see 'ps' man page\n"); + printf("\tUID - UID of the process owner\n"); + printf("\tPID - PID of the process\n"); + printf("\tPPID - PID of the parent process\n"); + printf("\tVSZ - Virtual memory usage (kilobytes)\n"); + printf("\tRSS - Real memory usage (kilobytes)\n"); + printf("\t%%CPU - CPU usage\n"); + printf("\tCOMMAND - Command being run\n"); + printf("\tARGS - Full command line with arguments\n"); + return; } diff --git a/plugins/check_by_ssh.c b/plugins/check_by_ssh.c index 2bc38d49..74b7a46f 100644 --- a/plugins/check_by_ssh.c +++ b/plugins/check_by_ssh.c @@ -45,7 +45,8 @@ typedef struct { check_by_ssh_config config; } check_by_ssh_config_wrapper; static check_by_ssh_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/); +static check_by_ssh_config_wrapper + validate_arguments(check_by_ssh_config_wrapper /*config_wrapper*/); static command_construct comm_append(command_construct /*cmd*/, const char * /*str*/); static void print_help(void); @@ -90,7 +91,8 @@ int main(int argc, char **argv) { /* SSH returns 255 if connection attempt fails; include the first line of error output */ if (result == 255 && config.unknown_timeout) { - printf(_("SSH connection failed: %s\n"), chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); + printf(_("SSH connection failed: %s\n"), + chld_err.lines > 0 ? chld_err.line[0] : "(no error output)"); return STATE_UNKNOWN; } @@ -134,7 +136,8 @@ int main(int argc, char **argv) { puts(chld_out.line[i]); } } else { - printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), state_text(result), config.remotecmd, result); + printf(_("%s - check_by_ssh: Remote command '%s' returned status %d\n"), + state_text(result), config.remotecmd, result); } return result; /* return error status from remote command */ } @@ -160,9 +163,11 @@ int main(int argc, char **argv) { die(STATE_UNKNOWN, _("%s: Error parsing output\n"), progname); } - if (config.service[commands] && status_text && sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { - fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", (int)local_time, config.host_shortname, - config.service[commands++], cresult, status_text); + if (config.service[commands] && status_text && + sscanf(chld_out.line[i], "STATUS CODE: %d", &cresult) == 1) { + fprintf(file_pointer, "[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n", + (int)local_time, config.host_shortname, config.service[commands++], cresult, + status_text); } } @@ -172,34 +177,35 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = {{"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, - {"verbose", no_argument, 0, 'v'}, - {"fork", no_argument, 0, 'f'}, - {"timeout", required_argument, 0, 't'}, - {"unknown-timeout", no_argument, 0, 'U'}, - {"host", required_argument, 0, 'H'}, /* backward compatibility */ - {"hostname", required_argument, 0, 'H'}, - {"port", required_argument, 0, 'p'}, - {"output", required_argument, 0, 'O'}, - {"name", required_argument, 0, 'n'}, - {"services", required_argument, 0, 's'}, - {"identity", required_argument, 0, 'i'}, - {"user", required_argument, 0, 'u'}, - {"logname", required_argument, 0, 'l'}, - {"command", required_argument, 0, 'C'}, - {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ - {"skip-stdout", optional_argument, 0, 'S'}, - {"skip-stderr", optional_argument, 0, 'E'}, - {"warn-on-stderr", no_argument, 0, 'W'}, - {"proto1", no_argument, 0, '1'}, - {"proto2", no_argument, 0, '2'}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"ssh-option", required_argument, 0, 'o'}, - {"quiet", no_argument, 0, 'q'}, - {"configfile", optional_argument, 0, 'F'}, - {0, 0, 0, 0}}; + static struct option longopts[] = { + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"fork", no_argument, 0, 'f'}, + {"timeout", required_argument, 0, 't'}, + {"unknown-timeout", no_argument, 0, 'U'}, + {"host", required_argument, 0, 'H'}, /* backward compatibility */ + {"hostname", required_argument, 0, 'H'}, + {"port", required_argument, 0, 'p'}, + {"output", required_argument, 0, 'O'}, + {"name", required_argument, 0, 'n'}, + {"services", required_argument, 0, 's'}, + {"identity", required_argument, 0, 'i'}, + {"user", required_argument, 0, 'u'}, + {"logname", required_argument, 0, 'l'}, + {"command", required_argument, 0, 'C'}, + {"skip", optional_argument, 0, 'S'}, /* backwards compatibility */ + {"skip-stdout", optional_argument, 0, 'S'}, + {"skip-stderr", optional_argument, 0, 'E'}, + {"warn-on-stderr", no_argument, 0, 'W'}, + {"proto1", no_argument, 0, '1'}, + {"proto2", no_argument, 0, '2'}, + {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, + {"ssh-option", required_argument, 0, 'o'}, + {"quiet", no_argument, 0, 'q'}, + {"configfile", optional_argument, 0, 'F'}, + {0, 0, 0, 0}}; check_by_ssh_config_wrapper result = { .errorcode = OK, @@ -221,7 +227,8 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { int option = 0; while (true) { - int opt_index = getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option); + int opt_index = + getopt_long(argc, argv, "Vvh1246fqt:UH:O:p:i:u:l:C:S::E::n:s:o:F:", longopts, &option); if (opt_index == -1 || opt_index == EOF) { break; @@ -266,11 +273,13 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { char *p2; p1 = optarg; - result.config.service = realloc(result.config.service, (++result.config.number_of_services) * sizeof(char *)); + result.config.service = realloc(result.config.service, + (++result.config.number_of_services) * sizeof(char *)); while ((p2 = index(p1, ':'))) { *p2 = '\0'; result.config.service[result.config.number_of_services - 1] = p1; - result.config.service = realloc(result.config.service, (++result.config.number_of_services) * sizeof(char *)); + result.config.service = realloc( + result.config.service, (++result.config.number_of_services) * sizeof(char *)); p1 = p2 + 1; } result.config.service[result.config.number_of_services - 1] = p1; @@ -309,7 +318,8 @@ check_by_ssh_config_wrapper process_arguments(int argc, char **argv) { case 'C': /* Command for remote machine */ result.config.commands++; if (result.config.commands > 1) { - xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", result.config.remotecmd); + xasprintf(&result.config.remotecmd, "%s;echo STATUS CODE: $?;", + result.config.remotecmd); } xasprintf(&result.config.remotecmd, "%s%s", result.config.remotecmd, optarg); break; @@ -396,7 +406,8 @@ command_construct comm_append(command_construct cmd, const char *str) { die(STATE_UNKNOWN, _("%s: Argument limit of %d exceeded\n"), progname, NP_MAXARGS); } - if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) == NULL) { + if ((cmd.commargv = (char **)realloc(cmd.commargv, (cmd.commargc + 1) * sizeof(char *))) == + NULL) { die(STATE_UNKNOWN, _("Can not (re)allocate 'commargv' buffer\n")); } @@ -412,12 +423,18 @@ check_by_ssh_config_wrapper validate_arguments(check_by_ssh_config_wrapper confi return config_wrapper; } - if (config_wrapper.config.passive && config_wrapper.config.commands != config_wrapper.config.number_of_services) { - die(STATE_UNKNOWN, _("%s: In passive mode, you must provide a service name for each command.\n"), progname); + if (config_wrapper.config.passive && + config_wrapper.config.commands != config_wrapper.config.number_of_services) { + die(STATE_UNKNOWN, + _("%s: In passive mode, you must provide a service name for each command.\n"), + progname); } if (config_wrapper.config.passive && config_wrapper.config.host_shortname == NULL) { - die(STATE_UNKNOWN, _("%s: In passive mode, you must provide the host short name from the monitoring configs.\n"), progname); + die(STATE_UNKNOWN, + _("%s: In passive mode, you must provide the host short name from the monitoring " + "configs.\n"), + progname); } return config_wrapper; @@ -454,7 +471,8 @@ void print_help(void) { printf(" %s\n", "-W, --warn-on-stderr]"); printf(" %s\n", _("Exit with an warning, if there is an output on STDERR")); printf(" %s\n", "-f"); - printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always return OK if ssh is executed")); + printf(" %s\n", _("tells ssh to fork rather than create a tty [optional]. This will always " + "return OK if ssh is executed")); printf(" %s\n", "-C, --command='COMMAND STRING'"); printf(" %s\n", _("command to execute on the remote machine")); printf(" %s\n", "-l, --logname=USERNAME"); @@ -490,7 +508,9 @@ void print_help(void) { printf(" %s\n", _("all of -O, -s, and -n options (servicelist order must match '-C'options)")); printf("\n"); printf("%s\n", _("Examples:")); - printf(" %s\n", "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); + printf( + " %s\n", + "$ check_by_ssh -H localhost -n lh -s c1:c2:c3 -C uptime -C uptime -C uptime -O /tmp/foo"); printf(" %s\n", "$ cat /tmp/foo"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c1;0; up 2 days"); printf(" %s\n", "[1080933700] PROCESS_SERVICE_CHECK_RESULT;flint;c2;0; up 2 days"); diff --git a/plugins/check_cluster.c b/plugins/check_cluster.c index 9b695499..373520ee 100644 --- a/plugins/check_cluster.c +++ b/plugins/check_cluster.c @@ -112,25 +112,30 @@ int main(int argc, char **argv) { int return_code = STATE_OK; /* return the status of the cluster */ if (config.check_type == CHECK_SERVICES) { - return_code = get_status(total_services_warning + total_services_unknown + total_services_critical, config.thresholds); - printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", state_text(return_code), - (config.label == NULL) ? "Service cluster" : config.label, total_services_ok, total_services_warning, total_services_unknown, + return_code = + get_status(total_services_warning + total_services_unknown + total_services_critical, + config.thresholds); + printf("CLUSTER %s: %s: %d ok, %d warning, %d unknown, %d critical\n", + state_text(return_code), (config.label == NULL) ? "Service cluster" : config.label, + total_services_ok, total_services_warning, total_services_unknown, total_services_critical); } else { return_code = get_status(total_hosts_down + total_hosts_unreachable, config.thresholds); printf("CLUSTER %s: %s: %d up, %d down, %d unreachable\n", state_text(return_code), - (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up, total_hosts_down, total_hosts_unreachable); + (config.label == NULL) ? "Host cluster" : config.label, total_hosts_up, + total_hosts_down, total_hosts_unreachable); } exit(return_code); } check_cluster_config_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = {{"data", required_argument, 0, 'd'}, {"warning", required_argument, 0, 'w'}, - {"critical", required_argument, 0, 'c'}, {"label", required_argument, 0, 'l'}, - {"host", no_argument, 0, 'h'}, {"service", no_argument, 0, 's'}, - {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}}; + static struct option longopts[] = { + {"data", required_argument, 0, 'd'}, {"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, {"label", required_argument, 0, 'l'}, + {"host", no_argument, 0, 'h'}, {"service", no_argument, 0, 's'}, + {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'H'}, {0, 0, 0, 0}}; check_cluster_config_wrapper result = { .errorcode = OK, @@ -251,7 +256,8 @@ void print_help(void) { printf("\n"); printf("%s\n", _("Examples:")); printf(" %s\n", "check_cluster -s -d 2,0,2,0 -c @3:"); - printf(" %s\n", _("Will alert critical if there are 3 or more service data points in a non-OK")); + printf(" %s\n", + _("Will alert critical if there are 3 or more service data points in a non-OK")); printf(" %s\n", _("state.")); printf(UT_SUPPORT); diff --git a/plugins/check_dbi.c b/plugins/check_dbi.c index 9efcd1cb..468ded31 100644 --- a/plugins/check_dbi.c +++ b/plugins/check_dbi.c @@ -71,8 +71,9 @@ static double timediff(struct timeval /*start*/, struct timeval /*end*/); static void np_dbi_print_error(dbi_conn /*conn*/, char * /*fmt*/, ...); -static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/, double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/, - mp_dbi_type /*type*/, char * /*np_dbi_query*/); +static mp_state_enum do_query(dbi_conn /*conn*/, const char ** /*res_val_str*/, + double * /*res_val*/, double * /*res_time*/, mp_dbi_metric /*metric*/, + mp_dbi_type /*type*/, char * /*np_dbi_query*/); int main(int argc, char **argv) { int status = STATE_UNKNOWN; @@ -118,7 +119,8 @@ int main(int argc, char **argv) { dbi_inst *instance_p = {0}; if (dbi_initialize_r(NULL, instance_p) < 0) { - printf("UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); + printf( + "UNKNOWN - failed to initialize DBI; possibly you don't have any drivers installed.\n"); return STATE_UNKNOWN; } @@ -133,10 +135,12 @@ int main(int argc, char **argv) { driver = dbi_driver_open_r(config.dbi_driver, instance_p); if (!driver) { - printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", config.dbi_driver); + printf("UNKNOWN - failed to open DBI driver '%s'; possibly it's not installed.\n", + config.dbi_driver); printf("Known drivers:\n"); - for (driver = dbi_driver_list_r(NULL, instance_p); driver; driver = dbi_driver_list_r(driver, instance_p)) { + for (driver = dbi_driver_list_r(NULL, instance_p); driver; + driver = dbi_driver_list_r(driver, instance_p)) { printf(" - %s\n", dbi_driver_get_name(driver)); } return STATE_UNKNOWN; @@ -156,7 +160,8 @@ int main(int argc, char **argv) { const char *opt; if (verbose > 1) { - printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key, config.dbi_options[i].value); + printf("Setting DBI driver option '%s' to '%s'\n", config.dbi_options[i].key, + config.dbi_options[i].value); } if (!dbi_conn_set_option(conn, config.dbi_options[i].key, config.dbi_options[i].value)) { @@ -164,10 +169,12 @@ int main(int argc, char **argv) { } /* else: status != 0 */ - np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", config.dbi_options[i].key, config.dbi_options[i].value); + np_dbi_print_error(conn, "UNKNOWN - failed to set option '%s' to '%s'", + config.dbi_options[i].key, config.dbi_options[i].value); printf("Known driver options:\n"); - for (opt = dbi_conn_get_option_list(conn, NULL); opt; opt = dbi_conn_get_option_list(conn, opt)) { + for (opt = dbi_conn_get_option_list(conn, NULL); opt; + opt = dbi_conn_get_option_list(conn, opt)) { printf(" - %s\n", opt); } dbi_conn_close(conn); @@ -230,14 +237,16 @@ int main(int argc, char **argv) { } if (dbi_conn_select_db(conn, config.dbi_database)) { - np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", config.dbi_database); + np_dbi_print_error(conn, "UNKNOWN - failed to select database '%s'", + config.dbi_database); return STATE_UNKNOWN; } } if (config.dbi_query) { /* execute query */ - status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, config.dbi_query); + status = do_query(conn, &query_val_str, &query_val, &query_time, config.metric, config.type, + config.dbi_query); if (status != STATE_OK) { /* do_query prints an error message in this case */ return status; @@ -281,7 +290,8 @@ int main(int argc, char **argv) { /* In case of METRIC_QUERY_RESULT, isnan(query_val) indicates an error * which should have been reported and handled (abort) before * ... unless we expected a string to be returned */ - assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || (config.type == TYPE_STRING)); + assert((config.metric != METRIC_QUERY_RESULT) || (!isnan(query_val)) || + (config.type == TYPE_STRING)); assert((config.type != TYPE_STRING) || (config.expect || config.expect_re_str)); @@ -289,12 +299,14 @@ int main(int argc, char **argv) { if (config.dbi_query) { if (config.type == TYPE_STRING) { assert(config.expect || config.expect_re_str); - printf(", '%s' returned '%s' in %fs", config.dbi_query, query_val_str ? query_val_str : "", query_time); + printf(", '%s' returned '%s' in %fs", config.dbi_query, + query_val_str ? query_val_str : "", query_time); if (status != STATE_OK) { if (config.expect) { printf(" (expected '%s')", config.expect); } else if (config.expect_re_str) { - printf(" (expected regex /%s/%s)", config.expect_re_str, ((config.expect_re_cflags & REG_ICASE) ? "i" : "")); + printf(" (expected regex /%s/%s)", config.expect_re_str, + ((config.expect_re_cflags & REG_ICASE) ? "i" : "")); } } } else if (isnan(query_val)) { @@ -304,18 +316,31 @@ int main(int argc, char **argv) { } } - printf(" | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, - ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "", - ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "", server_version, - ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range : "", - ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range : ""); + printf( + " | conntime=%fs;%s;%s;0; server_version=%u;%s;%s;0;", conn_time, + ((config.metric == METRIC_CONN_TIME) && config.warning_range) ? config.warning_range : "", + ((config.metric == METRIC_CONN_TIME) && config.critical_range) ? config.critical_range : "", + server_version, + ((config.metric == METRIC_SERVER_VERSION) && config.warning_range) ? config.warning_range + : "", + ((config.metric == METRIC_SERVER_VERSION) && config.critical_range) ? config.critical_range + : ""); if (config.dbi_query) { if (!isnan(query_val)) { /* this is also true when -e is used */ - printf(" query=%f;%s;%s;;", query_val, ((config.metric == METRIC_QUERY_RESULT) && config.warning_range) ? config.warning_range : "", - ((config.metric == METRIC_QUERY_RESULT) && config.critical_range) ? config.critical_range : ""); + printf(" query=%f;%s;%s;;", query_val, + ((config.metric == METRIC_QUERY_RESULT) && config.warning_range) + ? config.warning_range + : "", + ((config.metric == METRIC_QUERY_RESULT) && config.critical_range) + ? config.critical_range + : ""); } - printf(" querytime=%fs;%s;%s;0;", query_time, ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range : "", - ((config.metric == METRIC_QUERY_TIME) && config.critical_range) ? config.critical_range : ""); + printf(" querytime=%fs;%s;%s;0;", query_time, + ((config.metric == METRIC_QUERY_TIME) && config.warning_range) ? config.warning_range + : "", + ((config.metric == METRIC_QUERY_TIME) && config.critical_range) + ? config.critical_range + : ""); } printf("\n"); return status; @@ -442,7 +467,8 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { *value = '\0'; ++value; - new = realloc(result.config.dbi_options, (result.config.dbi_options_num + 1) * sizeof(*new)); + new = realloc(result.config.dbi_options, + (result.config.dbi_options_num + 1) * sizeof(*new)); if (!new) { printf("UNKNOWN - failed to reallocate memory\n"); exit(STATE_UNKNOWN); @@ -464,7 +490,8 @@ check_dbi_config_wrapper process_arguments(int argc, char **argv) { } } - set_thresholds(&result.config.dbi_thresholds, result.config.warning_range, result.config.critical_range); + set_thresholds(&result.config.dbi_thresholds, result.config.warning_range, + result.config.critical_range); return validate_arguments(result); } @@ -474,21 +501,28 @@ check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrap usage("Must specify a DBI driver"); } - if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) || (config_wrapper.config.metric == METRIC_QUERY_TIME)) && + if (((config_wrapper.config.metric == METRIC_QUERY_RESULT) || + (config_wrapper.config.metric == METRIC_QUERY_TIME)) && (!config_wrapper.config.dbi_query)) { usage("Must specify a query to execute (metric == QUERY_RESULT)"); } - if ((config_wrapper.config.metric != METRIC_CONN_TIME) && (config_wrapper.config.metric != METRIC_SERVER_VERSION) && - (config_wrapper.config.metric != METRIC_QUERY_RESULT) && (config_wrapper.config.metric != METRIC_QUERY_TIME)) { + if ((config_wrapper.config.metric != METRIC_CONN_TIME) && + (config_wrapper.config.metric != METRIC_SERVER_VERSION) && + (config_wrapper.config.metric != METRIC_QUERY_RESULT) && + (config_wrapper.config.metric != METRIC_QUERY_TIME)) { usage("Invalid metric specified"); } - if (config_wrapper.config.expect && (config_wrapper.config.warning_range || config_wrapper.config.critical_range || config_wrapper.config.expect_re_str)) { + if (config_wrapper.config.expect && + (config_wrapper.config.warning_range || config_wrapper.config.critical_range || + config_wrapper.config.expect_re_str)) { usage("Do not mix -e and -w/-c/-r/-R"); } - if (config_wrapper.config.expect_re_str && (config_wrapper.config.warning_range || config_wrapper.config.critical_range || config_wrapper.config.expect)) { + if (config_wrapper.config.expect_re_str && + (config_wrapper.config.warning_range || config_wrapper.config.critical_range || + config_wrapper.config.expect)) { usage("Do not mix -r/-R and -w/-c/-e"); } @@ -496,7 +530,8 @@ check_dbi_config_wrapper validate_arguments(check_dbi_config_wrapper config_wrap usage("Option -e requires metric QUERY_RESULT"); } - if (config_wrapper.config.expect_re_str && (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { + if (config_wrapper.config.expect_re_str && + (config_wrapper.config.metric != METRIC_QUERY_RESULT)) { usage("Options -r/-R require metric QUERY_RESULT"); } @@ -607,7 +642,8 @@ void print_usage(void) { printf(" [-e ] [-r|-R ]\n"); } -const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type, mp_dbi_metric metric, mp_dbi_type type) { +const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_type, + mp_dbi_metric metric, mp_dbi_type type) { const char *str; if (field_type != DBI_TYPE_STRING) { @@ -630,7 +666,8 @@ const char *get_field_str(dbi_conn conn, dbi_result res, unsigned short field_ty return str; } -double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric, mp_dbi_type type) { +double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_dbi_metric metric, + mp_dbi_type type) { double val = NAN; if (*field_type == DBI_TYPE_INTEGER) { @@ -679,7 +716,8 @@ double get_field(dbi_conn conn, dbi_result res, unsigned short *field_type, mp_d return val; } -mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, double *res_val, mp_dbi_metric metric, mp_dbi_type type) { +mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_val_str, + double *res_val, mp_dbi_metric metric, mp_dbi_type type) { unsigned short field_type; double val = NAN; @@ -747,8 +785,8 @@ mp_state_enum get_query_result(dbi_conn conn, dbi_result res, const char **res_v return STATE_OK; } -mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time, mp_dbi_metric metric, mp_dbi_type type, - char *np_dbi_query) { +mp_state_enum do_query(dbi_conn conn, const char **res_val_str, double *res_val, double *res_time, + mp_dbi_metric metric, mp_dbi_type type, char *np_dbi_query) { dbi_result res; struct timeval timeval_start; diff --git a/plugins/check_dig.c b/plugins/check_dig.c index d0903be2..c27e5f13 100644 --- a/plugins/check_dig.c +++ b/plugins/check_dig.c @@ -81,8 +81,9 @@ int main(int argc, char **argv) { char *command_line; /* get the command to run */ - xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG, config.dig_args, config.query_transport, - config.server_port, config.dns_server, config.query_address, config.record_type, config.number_tries, timeout_interval_dig); + xasprintf(&command_line, "%s %s %s -p %d @%s %s %s +retry=%d +time=%d", PATH_TO_DIG, + config.dig_args, config.query_transport, config.server_port, config.dns_server, + config.query_address, config.record_type, config.number_tries, timeout_interval_dig); alarm(timeout_interval); struct timeval start_time; @@ -118,8 +119,9 @@ int main(int argc, char **argv) { printf("%s\n", chld_out.line[i]); } - if (strcasestr(chld_out.line[i], (config.expected_address == NULL ? config.query_address : config.expected_address)) != - NULL) { + if (strcasestr(chld_out.line[i], (config.expected_address == NULL + ? config.query_address + : config.expected_address)) != NULL) { msg = chld_out.line[i]; result = STATE_OK; @@ -174,8 +176,9 @@ int main(int argc, char **argv) { printf("DNS %s - %.3f seconds response time (%s)|%s\n", state_text(result), elapsed_time, msg ? msg : _("Probably a non-existent host/domain"), - fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED), config.warning_interval, - (config.critical_interval > UNDEFINED), config.critical_interval, true, 0, false, 0)); + fperfdata("time", elapsed_time, "s", (config.warning_interval > UNDEFINED), + config.warning_interval, (config.critical_interval > UNDEFINED), + config.critical_interval, true, 0, false, 0)); exit(result); } @@ -335,7 +338,8 @@ void print_help(void) { printf(" %s\n", "-T, --record_type=STRING"); printf(" %s\n", _("Record type to lookup (default: A)")); printf(" %s\n", "-a, --expected_address=STRING"); - printf(" %s\n", _("An address expected to be in the answer section. If not set, uses whatever")); + printf(" %s\n", + _("An address expected to be in the answer section. If not set, uses whatever")); printf(" %s\n", _("was in -l")); printf(" %s\n", "-A, --dig-arguments=STRING"); printf(" %s\n", _("Pass STRING as argument(s) to dig")); diff --git a/plugins/check_disk.c b/plugins/check_disk.c index 515ddff0..d42b5486 100644 --- a/plugins/check_disk.c +++ b/plugins/check_disk.c @@ -78,18 +78,21 @@ typedef struct { } check_disk_config_wrapper; static check_disk_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units, - char *warn_freespace_percent, char *crit_freespace_percent, char *warn_freeinodes_percent, +static void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, + char *crit_freespace_units, char *warn_freespace_percent, + char *crit_freespace_percent, char *warn_freeinodes_percent, char *crit_freeinodes_percent); static double calculate_percent(uintmax_t /*value*/, uintmax_t /*total*/); static bool stat_path(parameter_list_elem * /*parameters*/, bool /*ignore_missing*/); /* - * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control how reserved - * and inodes should be judged (ignored or not) + * Puts the values from a struct fs_usage into a parameter_list with an additional flag to control + * how reserved and inodes should be judged (ignored or not) */ -static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp, bool freespace_ignore_reserved); -static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit); +static parameter_list_elem get_path_stats(parameter_list_elem parameters, struct fs_usage fsp, + bool freespace_ignore_reserved); +static mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, + bool display_inodes_perfdata, byte_unit unit); void print_usage(void); static void print_help(void); @@ -111,7 +114,6 @@ const byte_unit TeraBytes_factor = 1000000000000; const byte_unit PetaBytes_factor = 1000000000000000; const byte_unit ExaBytes_factor = 1000000000000000000; - int main(int argc, char **argv) { setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -140,7 +142,8 @@ int main(int argc, char **argv) { } if (!config.path_ignored) { - mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list, config.exact_match); + mp_int_fs_list_set_best_match(config.path_select_list, config.mount_list, + config.exact_match); } // Error if no match found for specified paths @@ -203,20 +206,23 @@ int main(int argc, char **argv) { } if (path->group == NULL) { - if (config.fs_exclude_list && np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) { + if (config.fs_exclude_list && + np_find_regmatch(config.fs_exclude_list, mount_entry->me_type)) { // Skip excluded fs's path = mp_int_fs_list_del(&config.path_select_list, path); continue; } - if (config.device_path_exclude_list && (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) || - np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) { + if (config.device_path_exclude_list && + (np_find_name(config.device_path_exclude_list, mount_entry->me_devname) || + np_find_name(config.device_path_exclude_list, mount_entry->me_mountdir))) { // Skip excluded device or mount paths path = mp_int_fs_list_del(&config.path_select_list, path); continue; } - if (config.fs_include_list && !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) { + if (config.fs_include_list && + !np_find_regmatch(config.fs_include_list, mount_entry->me_type)) { // Skip not included fstypes path = mp_int_fs_list_del(&config.path_select_list, path); continue; @@ -259,8 +265,8 @@ int main(int argc, char **argv) { if (verbose >= 3) { printf("For %s, used_units=%lu free_units=%lu total_units=%lu " "fsp.fsu_blocksize=%lu\n", - mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes, filesystem->total_bytes, - fsp.fsu_blocksize); + mount_entry->me_mountdir, filesystem->used_bytes, filesystem->free_bytes, + filesystem->total_bytes, fsp.fsu_blocksize); } } else { // failed to retrieve file system data or not mounted? @@ -285,12 +291,14 @@ int main(int argc, char **argv) { measurement_unit_list *measurements = NULL; measurement_unit_list *current = NULL; // create measuring units, because of groups - for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; filesystem = mp_int_fs_list_get_next(filesystem)) { + for (parameter_list_elem *filesystem = config.path_select_list.first; filesystem; + filesystem = mp_int_fs_list_get_next(filesystem)) { assert(filesystem->best_match != NULL); if (filesystem->group == NULL) { // create a measurement unit for the fs - measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + measurement_unit unit = + create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); if (measurements == NULL) { measurements = current = add_measurement_list(NULL, unit); } else { @@ -300,14 +308,17 @@ int main(int argc, char **argv) { // Grouped elements are consecutive if (measurements == NULL) { // first entry - measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + measurement_unit unit = + create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); unit.name = strdup(filesystem->group); measurements = current = add_measurement_list(NULL, unit); } else { - // if this is the first element of a group, the name of the previous entry is different + // if this is the first element of a group, the name of the previous entry is + // different if (strcmp(filesystem->group, current->unit.name) != 0) { // so, this must be the first element of a group - measurement_unit unit = create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); + measurement_unit unit = + create_measurement_unit_from_filesystem(*filesystem, config.display_mntp); unit.name = filesystem->group; current = add_measurement_list(measurements, unit); @@ -322,7 +333,8 @@ int main(int argc, char **argv) { /* Process for every path in list */ if (measurements != NULL) { for (measurement_unit_list *unit = measurements; unit; unit = unit->next) { - mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata, config.display_unit); + mp_subcheck unit_sc = evaluate_filesystem(unit->unit, config.display_inodes_perfdata, + config.display_unit); mp_add_subcheck_to_check(&overall, unit_sc); } } else { @@ -433,7 +445,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { while (true) { int option = 0; - int option_index = getopt_long(argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); + int option_index = getopt_long( + argc, argv, "+?VqhvefCt:c:w:K:W:u:p:x:X:N:mklLPg:R:r:i:I:MEAn", longopts, &option); if (option_index == -1 || option_index == EOF) { break; @@ -578,9 +591,10 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.display_inodes_perfdata = true; break; case 'p': /* select path */ { - if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || - warn_freeinodes_percent || crit_freeinodes_percent)) { - die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set a threshold value before using -p\n")); + if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || + crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { + die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), + _("Must set a threshold value before using -p\n")); } /* add parameter if not found. overwrite thresholds if path has already been added */ @@ -595,7 +609,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { // } } search_entry->group = group; - set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + set_all_thresholds(search_entry, warn_freespace_units, crit_freespace_units, + warn_freespace_percent, crit_freespace_percent, warn_freeinodes_percent, crit_freeinodes_percent); @@ -603,7 +618,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { // if (!stat_path(se, result.config.ignore_missing)) { // break; // } - mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); + mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, + result.config.exact_match); path_selected = true; } break; @@ -615,7 +631,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (err != 0) { char errbuf[MAX_INPUT_BUFFER]; regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); - die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); + die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), + _("Could not compile regular expression"), errbuf); } break; case 'N': /* include file system type */ @@ -623,7 +640,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (err != 0) { char errbuf[MAX_INPUT_BUFFER]; regerror(err, &result.config.fs_exclude_list->regex, errbuf, MAX_INPUT_BUFFER); - die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); + die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), + _("Could not compile regular expression"), errbuf); } } break; case 'v': /* verbose */ @@ -638,7 +656,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { break; case 'E': if (path_selected) { - die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set -E before selecting paths\n")); + die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), + _("Must set -E before selecting paths\n")); } result.config.exact_match = true; break; @@ -647,7 +666,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { break; case 'g': if (path_selected) { - die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), _("Must set group value before selecting paths\n")); + die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), + _("Must set group value before selecting paths\n")); } group = optarg; break; @@ -657,14 +677,16 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { case 'i': { if (!path_selected) { die(STATE_UNKNOWN, "DISK %s: %s\n", _("UNKNOWN"), - _("Paths need to be selected before using -i/-I. Use -A to select all paths explicitly")); + _("Paths need to be selected before using -i/-I. Use -A to select all paths " + "explicitly")); } regex_t regex; int err = regcomp(®ex, optarg, cflags); if (err != 0) { char errbuf[MAX_INPUT_BUFFER]; regerror(err, ®ex, errbuf, MAX_INPUT_BUFFER); - die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); + die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), + _("Could not compile regular expression"), errbuf); } for (parameter_list_elem *elem = result.config.path_select_list.first; elem;) { @@ -695,10 +717,11 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { cflags |= REG_ICASE; // Intentional fallthrough case 'r': { - if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || crit_freespace_percent || - warn_freeinodes_percent || crit_freeinodes_percent)) { + if (!(warn_freespace_units || crit_freespace_units || warn_freespace_percent || + crit_freespace_percent || warn_freeinodes_percent || crit_freeinodes_percent)) { die(STATE_UNKNOWN, "DISK %s: %s", _("UNKNOWN"), - _("Must set a threshold value before using -r/-R/-A (--ereg-path/--eregi-path/--all)\n")); + _("Must set a threshold value before using -r/-R/-A " + "(--ereg-path/--eregi-path/--all)\n")); } regex_t regex; @@ -706,7 +729,8 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (err != 0) { char errbuf[MAX_INPUT_BUFFER]; regerror(err, ®ex, errbuf, MAX_INPUT_BUFFER); - die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Could not compile regular expression"), errbuf); + die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), + _("Could not compile regular expression"), errbuf); } bool found = false; @@ -714,16 +738,21 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (np_regex_match_mount_entry(me, ®ex)) { found = true; if (verbose >= 3) { - printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, optarg); + printf("%s %s matching expression %s\n", me->me_devname, me->me_mountdir, + optarg); } - /* add parameter if not found. overwrite thresholds if path has already been added */ + /* add parameter if not found. overwrite thresholds if path has already been + * added */ parameter_list_elem *se = NULL; - if (!(se = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { - se = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); + if (!(se = mp_int_fs_list_find(result.config.path_select_list, + me->me_mountdir))) { + se = + mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); } se->group = group; - set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + set_all_thresholds(se, warn_freespace_units, crit_freespace_units, + warn_freespace_percent, crit_freespace_percent, warn_freeinodes_percent, crit_freeinodes_percent); } } @@ -735,11 +764,13 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { break; } - die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), _("Regular expression did not match any path or disk"), optarg); + die(STATE_UNKNOWN, "DISK %s: %s - %s\n", _("UNKNOWN"), + _("Regular expression did not match any path or disk"), optarg); } path_selected = true; - mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, result.config.exact_match); + mp_int_fs_list_set_best_match(result.config.path_select_list, result.config.mount_list, + result.config.exact_match); cflags = default_cflags; } break; @@ -747,16 +778,20 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { result.config.display_mntp = true; break; case 'C': { - /* add all mount entries to path_select list if no partitions have been explicitly defined using -p */ + /* add all mount entries to path_select list if no partitions have been explicitly + * defined using -p */ if (!path_selected) { parameter_list_elem *path; for (struct mount_entry *me = result.config.mount_list; me; me = me->me_next) { - if (!(path = mp_int_fs_list_find(result.config.path_select_list, me->me_mountdir))) { - path = mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); + if (!(path = mp_int_fs_list_find(result.config.path_select_list, + me->me_mountdir))) { + path = + mp_int_fs_list_append(&result.config.path_select_list, me->me_mountdir); } path->best_match = me; path->group = group; - set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + set_all_thresholds(path, warn_freespace_units, crit_freespace_units, + warn_freespace_percent, crit_freespace_percent, warn_freeinodes_percent, crit_freeinodes_percent); } } @@ -843,10 +878,12 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { if (verbose > 0) { printf("Got an positional filesystem: %s\n", argv[index]); } - struct parameter_list *se = mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++])); + struct parameter_list *se = + mp_int_fs_list_append(&result.config.path_select_list, strdup(argv[index++])); path_selected = true; - set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, - warn_freeinodes_percent, crit_freeinodes_percent); + set_all_thresholds(se, warn_freespace_units, crit_freespace_units, warn_freespace_percent, + crit_freespace_percent, warn_freeinodes_percent, + crit_freeinodes_percent); } // If a list of paths has not been explicitly selected, find entire @@ -864,18 +901,21 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { } path->best_match = me; path->group = group; - set_all_thresholds(path, warn_freespace_units, crit_freespace_units, warn_freespace_percent, crit_freespace_percent, + set_all_thresholds(path, warn_freespace_units, crit_freespace_units, + warn_freespace_percent, crit_freespace_percent, warn_freeinodes_percent, crit_freeinodes_percent); } } // Set thresholds to the appropriate unit - for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp; tmp = mp_int_fs_list_get_next(tmp)) { + for (parameter_list_elem *tmp = result.config.path_select_list.first; tmp; + tmp = mp_int_fs_list_get_next(tmp)) { mp_perfdata_value factor = mp_create_pd_value(unit); if (tmp->freespace_units.critical_is_set) { - tmp->freespace_units.critical = mp_range_multiply(tmp->freespace_units.critical, factor); + tmp->freespace_units.critical = + mp_range_multiply(tmp->freespace_units.critical, factor); } if (tmp->freespace_units.warning_is_set) { tmp->freespace_units.warning = mp_range_multiply(tmp->freespace_units.warning, factor); @@ -885,8 +925,10 @@ check_disk_config_wrapper process_arguments(int argc, char **argv) { return result; } -void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, char *crit_freespace_units, char *warn_freespace_percent, - char *crit_freespace_percent, char *warn_freeinodes_percent, char *crit_freeinodes_percent) { +void set_all_thresholds(parameter_list_elem *path, char *warn_freespace_units, + char *crit_freespace_units, char *warn_freespace_percent, + char *crit_freespace_percent, char *warn_freeinodes_percent, + char *crit_freeinodes_percent) { mp_range_parsed tmp; if (warn_freespace_units) { @@ -927,7 +969,8 @@ void print_help(void) { printf(COPYRIGHT, copyright, email); printf("%s\n", _("This plugin checks the amount of used disk space on a mounted file system")); - printf("%s\n", _("and generates an alert if free space is less than one of the threshold values")); + printf("%s\n", + _("and generates an alert if free space is less than one of the threshold values")); printf("\n\n"); @@ -949,7 +992,8 @@ void print_help(void) { printf(" %s\n", "-K, --icritical=PERCENT%"); printf(" %s\n", _("Exit with CRITICAL status if less than PERCENT of inode space is free")); printf(" %s\n", "-p, --path=PATH, --partition=PARTITION"); - printf(" %s\n", _("Mount point or block device as emitted by the mount(8) command (may be repeated)")); + printf(" %s\n", + _("Mount point or block device as emitted by the mount(8) command (may be repeated)")); printf(" %s\n", "-x, --exclude_device=PATH "); printf(" %s\n", _("Ignore device (only works if -p unspecified)")); printf(" %s\n", "-C, --clear"); @@ -963,71 +1007,88 @@ void print_help(void) { printf(" %s\n", "-P, --iperfdata"); printf(" %s\n", _("Display inode usage in perfdata")); printf(" %s\n", "-g, --group=NAME"); - printf(" %s\n", _("Group paths. Thresholds apply to (free-)space of all partitions together")); + printf(" %s\n", + _("Group paths. Thresholds apply to (free-)space of all partitions together")); printf(" %s\n", "-l, --local"); printf(" %s\n", _("Only check local filesystems")); printf(" %s\n", "-L, --stat-remote-fs"); - printf(" %s\n", _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); + printf( + " %s\n", + _("Only check local filesystems against thresholds. Yet call stat on remote filesystems")); printf(" %s\n", _("to test if they are accessible (e.g. to detect Stale NFS Handles)")); printf(" %s\n", "-M, --mountpoint"); printf(" %s\n", _("Display the (block) device instead of the mount point")); printf(" %s\n", "-A, --all"); printf(" %s\n", _("Explicitly select all paths. This is equivalent to -R '.*'")); printf(" %s\n", "-R, --eregi-path=PATH, --eregi-partition=PARTITION"); - printf(" %s\n", _("Case insensitive regular expression for path/partition (may be repeated)")); + printf(" %s\n", + _("Case insensitive regular expression for path/partition (may be repeated)")); printf(" %s\n", "-r, --ereg-path=PATH, --ereg-partition=PARTITION"); printf(" %s\n", _("Regular expression for path or partition (may be repeated)")); printf(" %s\n", "-I, --ignore-eregi-path=PATH, --ignore-eregi-partition=PARTITION"); - printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) (may be repeated)")); + printf(" %s\n", _("Regular expression to ignore selected path/partition (case insensitive) " + "(may be repeated)")); printf(" %s\n", "-i, --ignore-ereg-path=PATH, --ignore-ereg-partition=PARTITION"); - printf(" %s\n", _("Regular expression to ignore selected path or partition (may be repeated)")); + printf(" %s\n", + _("Regular expression to ignore selected path or partition (may be repeated)")); printf(" %s\n", "-n, --ignore-missing"); - printf(" %s\n", _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible.")); + printf(" %s\n", + _("Return OK if no filesystem matches, filesystem does not exist or is inaccessible.")); printf(" %s\n", _("(Provide this option before -p / -r / --ereg-path if used)")); printf(UT_PLUG_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); printf(" %s\n", "-u, --units=STRING"); printf(" %s\n", _("Select the unit used for the absolute value thresholds")); - printf( - " %s\n", - _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); + printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", " + "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); printf(" %s\n", "-k, --kilobytes"); printf(" %s\n", _("Same as '--units kB'")); printf(" %s\n", "--display-unit"); printf(" %s\n", _("Select the unit used for in the output")); - printf( - " %s\n", - _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", \"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); + printf(" %s\n", _("Choose one of \"bytes\", \"KiB\", \"kB\", \"MiB\", \"MB\", \"GiB\", " + "\"GB\", \"TiB\", \"TB\", \"PiB\", \"PB\" (default: MiB)")); printf(" %s\n", "-m, --megabytes"); printf(" %s\n", _("Same as '--units MB'")); printf(UT_VERBOSE); printf(" %s\n", "-X, --exclude-type=TYPE_REGEX"); - printf(" %s\n", _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); + printf(" %s\n", + _("Ignore all filesystems of types matching given regex(7) (may be repeated)")); printf(" %s\n", "-N, --include-type=TYPE_REGEX"); - printf(" %s\n", _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); + printf( + " %s\n", + _("Check only filesystems where the type matches this given regex(7) (may be repeated)")); printf(UT_OUTPUT_FORMAT); printf("\n"); printf("%s\n", _("General usage hints:")); - printf(" %s\n", _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as")); + printf( + " %s\n", + _("- Arguments are positional! \"-w 5 -c 1 -p /foo -w6 -c2 -p /bar\" is not the same as")); printf(" %s\n", _("\"-w 5 -c 1 -p /bar w6 -c2 -p /foo\".")); - printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} {thresholds b} ...\"")); + printf(" %s\n", _("- The syntax is broadly: \"{thresholds a} {paths a} -C {thresholds b} " + "{thresholds b} ...\"")); printf("\n"); printf("%s\n", _("Examples:")); printf(" %s\n", "check_disk -w 10% -c 5% -p /tmp -p /var -C -w 100000 -c 50000 -p /"); printf(" %s\n\n", _("Checks /tmp and /var at 10% and 5%, and / at 100MB and 50MB")); - printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'"); - printf(" %s\n", _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); - printf(" %s\n\n", _("are grouped which means the freespace thresholds are applied to all disks together")); + printf(" %s\n", + "check_disk -w 100 -c 50 -C -w 1000 -c 500 -g sidDATA -r '^/oracle/SID/data.*$'"); + printf( + " %s\n", + _("Checks all filesystems not matching -r at 100M and 50M. The fs matching the -r regex")); + printf(" %s\n\n", + _("are grouped which means the freespace thresholds are applied to all disks together")); printf(" %s\n", "check_disk -w 100 -c 50 -C -w 1000 -c 500 -p /foo -C -w 5% -c 3% -p /bar"); - printf(" %s\n", _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); + printf(" %s\n", + _("Checks /foo for 1000M/500M and /bar for 5/3%. All remaining volumes use 100M/50M")); printf(UT_SUPPORT); } void print_usage(void) { printf("%s\n", _("Usage:")); - printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c absolute_limit|-c percentage_limit%% | -K " + printf(" %s {-w absolute_limit |-w percentage_limit%% | -W inode_percentage_limit } {-c " + "absolute_limit|-c percentage_limit%% | -K " "inode_percentage_limit } {-p path | -x device}\n", progname); printf("[-C] [-E] [-e] [-f] [-g group ] [-k] [-l] [-M] [-m] [-R path ] [-r path ]\n"); @@ -1049,13 +1110,15 @@ bool stat_path(parameter_list_elem *parameters, bool ignore_missing) { return false; } printf("DISK %s - ", _("CRITICAL")); - die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"), strerror(errno)); + die(STATE_CRITICAL, _("%s %s: %s\n"), parameters->name, _("is not accessible"), + strerror(errno)); } return true; } -static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp, bool freespace_ignore_reserved) { +static parameter_list_elem get_path_stats(parameter_list_elem parameters, const struct fs_usage fsp, + bool freespace_ignore_reserved) { uintmax_t available = fsp.fsu_bavail; uintmax_t available_to_root = fsp.fsu_bfree; uintmax_t used = fsp.fsu_blocks - fsp.fsu_bfree; @@ -1082,7 +1145,8 @@ static parameter_list_elem get_path_stats(parameter_list_elem parameters, const /* option activated : we subtract the root-reserved inodes from the total */ /* not all OS report fsp->fsu_favail, only the ones with statvfs syscall */ /* for others, fsp->fsu_ffree == fsp->fsu_favail */ - parameters.inodes_total = fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free; + parameters.inodes_total = + fsp.fsu_files - parameters.inodes_free_to_root + parameters.inodes_free; } else { /* default behaviour : take all the inodes into account */ parameters.inodes_total = fsp.fsu_files; @@ -1091,7 +1155,8 @@ static parameter_list_elem get_path_stats(parameter_list_elem parameters, const return parameters; } -mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, byte_unit unit) { +mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_inodes_perfdata, + byte_unit unit) { mp_subcheck result = mp_subcheck_init(); result = mp_set_subcheck_default_state(result, STATE_UNKNOWN); xasprintf(&result.output, "%s", measurement_unit.name); @@ -1108,10 +1173,12 @@ mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_ freespace_bytes_sc = mp_set_subcheck_default_state(freespace_bytes_sc, STATE_OK); if (unit != Humanized) { - xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)", (uintmax_t)(measurement_unit.free_bytes / unit), - get_unit_string(unit), (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit)); + xasprintf(&freespace_bytes_sc.output, "Free space absolute: %ju%s (of %ju%s)", + (uintmax_t)(measurement_unit.free_bytes / unit), get_unit_string(unit), + (uintmax_t)(measurement_unit.total_bytes / unit), get_unit_string(unit)); } else { - xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)", humanize_byte_value(measurement_unit.free_bytes, false), + xasprintf(&freespace_bytes_sc.output, "Free space absolute: %s (of %s)", + humanize_byte_value(measurement_unit.free_bytes, false), humanize_byte_value((unsigned long long)measurement_unit.total_bytes, false)); } @@ -1127,29 +1194,37 @@ mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_ // special case for absolute space thresholds here: // if absolute values are not set, compute the thresholds from percentage thresholds mp_thresholds temp_thlds = measurement_unit.freespace_bytes_thresholds; - if (!temp_thlds.critical_is_set && measurement_unit.freespace_percent_thresholds.critical_is_set) { + if (!temp_thlds.critical_is_set && + measurement_unit.freespace_percent_thresholds.critical_is_set) { mp_range tmp_range = measurement_unit.freespace_percent_thresholds.critical; if (!tmp_range.end_infinity) { - tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes); + tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * + measurement_unit.total_bytes); } if (!tmp_range.start_infinity) { - tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes); + tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * + measurement_unit.total_bytes); } - measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range); + measurement_unit.freespace_bytes_thresholds = + mp_thresholds_set_crit(measurement_unit.freespace_bytes_thresholds, tmp_range); used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); } - if (!temp_thlds.warning_is_set && measurement_unit.freespace_percent_thresholds.warning_is_set) { + if (!temp_thlds.warning_is_set && + measurement_unit.freespace_percent_thresholds.warning_is_set) { mp_range tmp_range = measurement_unit.freespace_percent_thresholds.warning; if (!tmp_range.end_infinity) { - tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * measurement_unit.total_bytes); + tmp_range.end = mp_create_pd_value(mp_get_pd_value(tmp_range.end) / 100 * + measurement_unit.total_bytes); } if (!tmp_range.start_infinity) { - tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * measurement_unit.total_bytes); + tmp_range.start = mp_create_pd_value(mp_get_pd_value(tmp_range.start) / 100 * + measurement_unit.total_bytes); } - measurement_unit.freespace_bytes_thresholds = mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range); + measurement_unit.freespace_bytes_thresholds = + mp_thresholds_set_warn(measurement_unit.freespace_bytes_thresholds, tmp_range); used_space = mp_pd_set_thresholds(used_space, measurement_unit.freespace_bytes_thresholds); } @@ -1161,15 +1236,18 @@ mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_ mp_subcheck freespace_percent_sc = mp_subcheck_init(); freespace_percent_sc = mp_set_subcheck_default_state(freespace_percent_sc, STATE_OK); - double free_percentage = calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes); + double free_percentage = + calculate_percent(measurement_unit.free_bytes, measurement_unit.total_bytes); xasprintf(&freespace_percent_sc.output, "Free space percentage: %g%%", free_percentage); // Using perfdata here just to get to the test result mp_perfdata free_space_percent_pd = perfdata_init(); free_space_percent_pd.value = mp_create_pd_value(free_percentage); - free_space_percent_pd = mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds); + free_space_percent_pd = + mp_pd_set_thresholds(free_space_percent_pd, measurement_unit.freespace_percent_thresholds); - freespace_percent_sc = mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd)); + freespace_percent_sc = + mp_set_subcheck_state(freespace_percent_sc, mp_get_pd_status(free_space_percent_pd)); mp_add_subcheck_to_subcheck(&result, freespace_percent_sc); // ================ @@ -1181,35 +1259,41 @@ mp_subcheck evaluate_filesystem(measurement_unit measurement_unit, bool display_ mp_subcheck freeindodes_percent_sc = mp_subcheck_init(); freeindodes_percent_sc = mp_set_subcheck_default_state(freeindodes_percent_sc, STATE_OK); - double free_inode_percentage = calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total); + double free_inode_percentage = + calculate_percent(measurement_unit.inodes_free, measurement_unit.inodes_total); if (verbose > 0) { printf("free inode percentage computed: %g\n", free_inode_percentage); } - xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)", free_inode_percentage, measurement_unit.inodes_free, + xasprintf(&freeindodes_percent_sc.output, "Inodes free: %g%% (%ju of %ju)", + free_inode_percentage, measurement_unit.inodes_free, measurement_unit.inodes_total); mp_perfdata inodes_pd = perfdata_init(); xasprintf(&inodes_pd.label, "%s (inodes)", measurement_unit.name); inodes_pd = mp_set_pd_value(inodes_pd, measurement_unit.inodes_used); - inodes_pd = mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total)); + inodes_pd = + mp_set_pd_max_value(inodes_pd, mp_create_pd_value(measurement_unit.inodes_total)); inodes_pd = mp_set_pd_min_value(inodes_pd, mp_create_pd_value(0)); mp_thresholds absolut_inode_thresholds = measurement_unit.freeinodes_percent_thresholds; if (absolut_inode_thresholds.critical_is_set) { absolut_inode_thresholds.critical = - mp_range_multiply(absolut_inode_thresholds.critical, mp_create_pd_value(measurement_unit.inodes_total / 100)); + mp_range_multiply(absolut_inode_thresholds.critical, + mp_create_pd_value(measurement_unit.inodes_total / 100)); } if (absolut_inode_thresholds.warning_is_set) { absolut_inode_thresholds.warning = - mp_range_multiply(absolut_inode_thresholds.warning, mp_create_pd_value(measurement_unit.inodes_total / 100)); + mp_range_multiply(absolut_inode_thresholds.warning, + mp_create_pd_value(measurement_unit.inodes_total / 100)); } inodes_pd = mp_pd_set_thresholds(inodes_pd, absolut_inode_thresholds); - freeindodes_percent_sc = mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd)); + freeindodes_percent_sc = + mp_set_subcheck_state(freeindodes_percent_sc, mp_get_pd_status(inodes_pd)); if (display_inodes_perfdata) { mp_add_perfdata_to_subcheck(&freeindodes_percent_sc, inodes_pd); } diff --git a/plugins/check_disk.d/utils_disk.c b/plugins/check_disk.d/utils_disk.c index eec1282b..0b89018d 100644 --- a/plugins/check_disk.d/utils_disk.c +++ b/plugins/check_disk.d/utils_disk.c @@ -126,7 +126,8 @@ bool np_find_regmatch(struct regex_list *list, const char *name) { /* Emulate a full match as if surrounded with ^( )$ by checking whether the match spans the whole name */ regmatch_t dummy_match; - if (!regexec(&list->regex, name, 1, &dummy_match, 0) && dummy_match.rm_so == 0 && dummy_match.rm_eo == len) { + if (!regexec(&list->regex, name, 1, &dummy_match, 0) && dummy_match.rm_so == 0 && + dummy_match.rm_eo == len) { return true; } } @@ -144,7 +145,8 @@ bool np_seen_name(struct name_list *list, const char *name) { } bool np_regex_match_mount_entry(struct mount_entry *me, regex_t *re) { - return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0)); + return ((regexec(re, me->me_devname, (size_t)0, NULL, 0) == 0) || + (regexec(re, me->me_mountdir, (size_t)0, NULL, 0) == 0)); } check_disk_config check_disk_config_init() { @@ -264,7 +266,8 @@ measurement_unit_list *add_measurement_list(measurement_unit_list *list, measure return new; } -measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, parameter_list_elem filesystem) { +measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, + parameter_list_elem filesystem) { unit.free_bytes += filesystem.free_bytes; unit.used_bytes += filesystem.used_bytes; @@ -277,7 +280,8 @@ measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, param return unit; } -measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, bool display_mntp) { +measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, + bool display_mntp) { measurement_unit result = measurement_unit_init(); if (!display_mntp) { result.name = strdup(filesystem.best_match->me_mountdir); @@ -469,17 +473,20 @@ parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current) { return current->next; } -void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, bool exact) { +void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, + bool exact) { for (parameter_list_elem *elem = list.first; elem; elem = mp_int_fs_list_get_next(elem)) { if (!elem->best_match) { size_t name_len = strlen(elem->name); struct mount_entry *best_match = NULL; /* set best match if path name exactly matches a mounted device name */ - for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { + for (struct mount_entry *mount_entry = mount_list; mount_entry; + mount_entry = mount_entry->me_next) { if (strcmp(mount_entry->me_devname, elem->name) == 0) { struct fs_usage fsp; - if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) { + if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= + 0) { best_match = mount_entry; } } @@ -488,15 +495,18 @@ void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mou /* set best match by directory name if no match was found by devname */ if (!best_match) { size_t best_match_len = 0; - for (struct mount_entry *mount_entry = mount_list; mount_entry; mount_entry = mount_entry->me_next) { + for (struct mount_entry *mount_entry = mount_list; mount_entry; + mount_entry = mount_entry->me_next) { size_t len = strlen(mount_entry->me_mountdir); - if ((!exact && (best_match_len <= len && len <= name_len && - (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) || + if ((!exact && + (best_match_len <= len && len <= name_len && + (len == 1 || strncmp(mount_entry->me_mountdir, elem->name, len) == 0))) || (exact && strcmp(mount_entry->me_mountdir, elem->name) == 0)) { struct fs_usage fsp; - if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= 0) { + if (get_fs_usage(mount_entry->me_mountdir, mount_entry->me_devname, &fsp) >= + 0) { best_match = mount_entry; best_match_len = len; } @@ -507,7 +517,8 @@ void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mou if (best_match) { elem->best_match = best_match; } else { - elem->best_match = NULL; /* Not sure why this is needed as it should be null on initialisation */ + elem->best_match = + NULL; /* Not sure why this is needed as it should be null on initialisation */ } // No filesystem without a mount_entry! diff --git a/plugins/check_disk.d/utils_disk.h b/plugins/check_disk.d/utils_disk.h index 6831d1fd..c96d4296 100644 --- a/plugins/check_disk.d/utils_disk.h +++ b/plugins/check_disk.d/utils_disk.h @@ -141,12 +141,15 @@ parameter_list_elem *mp_int_fs_list_append(filesystem_list *list, const char *na parameter_list_elem *mp_int_fs_list_find(filesystem_list list, const char *name); parameter_list_elem *mp_int_fs_list_del(filesystem_list *list, parameter_list_elem *item); parameter_list_elem *mp_int_fs_list_get_next(parameter_list_elem *current); -void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, bool exact); +void mp_int_fs_list_set_best_match(filesystem_list list, struct mount_entry *mount_list, + bool exact); measurement_unit measurement_unit_init(); measurement_unit_list *add_measurement_list(measurement_unit_list *list, measurement_unit elem); -measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, parameter_list_elem filesystem); -measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, bool display_mntp); +measurement_unit add_filesystem_to_measurement_unit(measurement_unit unit, + parameter_list_elem filesystem); +measurement_unit create_measurement_unit_from_filesystem(parameter_list_elem filesystem, + bool display_mntp); int search_parameter_list(parameter_list_elem *list, const char *name); bool np_regex_match_mount_entry(struct mount_entry *, regex_t *); diff --git a/plugins/check_dns.c b/plugins/check_dns.c index 95f33083..56f91dae 100644 --- a/plugins/check_dns.c +++ b/plugins/check_dns.c @@ -48,7 +48,8 @@ typedef struct { } check_dns_config_wrapper; static check_dns_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); static check_dns_config_wrapper validate_arguments(check_dns_config_wrapper /*config_wrapper*/); -static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/, const char /*dns_server*/[ADDRESS_LENGTH]); +static mp_state_enum error_scan(char * /*input_buffer*/, bool * /*is_nxdomain*/, + const char /*dns_server*/[ADDRESS_LENGTH]); static bool ip_match_cidr(const char * /*addr*/, const char * /*cidr_ro*/); static unsigned long ip2long(const char * /*src*/); static void print_help(void); @@ -127,7 +128,8 @@ int main(int argc, char **argv) { puts(chld_out.line[i]); } - if (strcasestr(chld_out.line[i], ".in-addr.arpa") || strcasestr(chld_out.line[i], ".ip6.arpa")) { + if (strcasestr(chld_out.line[i], ".in-addr.arpa") || + strcasestr(chld_out.line[i], ".ip6.arpa")) { if ((strstr(chld_out.line[i], "canonical name = ") != NULL)) { continue; } @@ -145,7 +147,8 @@ int main(int argc, char **argv) { if (strstr(chld_out.line[i], "Server:") && strlen(config.dns_server) > 0) { char *temp_buffer = strchr(chld_out.line[i], ':'); if (temp_buffer == NULL) { - die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"), NSLOOKUP_COMMAND); + die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Server line\n"), + NSLOOKUP_COMMAND); } temp_buffer++; @@ -157,21 +160,25 @@ int main(int argc, char **argv) { strip(temp_buffer); if (strlen(temp_buffer) == 0) { - die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"), NSLOOKUP_COMMAND); + die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty server string\n"), + NSLOOKUP_COMMAND); } if (strcmp(temp_buffer, config.dns_server) != 0) { - die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), config.dns_server); + die(STATE_CRITICAL, _("DNS CRITICAL - No response from DNS %s\n"), + config.dns_server); } } /* the server is responding, we just got the host name... */ if (strstr(chld_out.line[i], "Name:")) { parse_address = true; - } else if (parse_address && (strstr(chld_out.line[i], "Address:") || strstr(chld_out.line[i], "Addresses:"))) { + } else if (parse_address && (strstr(chld_out.line[i], "Address:") || + strstr(chld_out.line[i], "Addresses:"))) { char *temp_buffer = strchr(chld_out.line[i], ':'); if (temp_buffer == NULL) { - die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"), NSLOOKUP_COMMAND); + die(STATE_UNKNOWN, _("'%s' returned a weirdly formatted Address line\n"), + NSLOOKUP_COMMAND); } temp_buffer++; @@ -183,7 +190,8 @@ int main(int argc, char **argv) { strip(temp_buffer); if (strlen(temp_buffer) == 0) { - die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"), NSLOOKUP_COMMAND); + die(STATE_CRITICAL, _("DNS CRITICAL - '%s' returned empty host name string\n"), + NSLOOKUP_COMMAND); } addresses[n_addresses++] = strdup(temp_buffer); @@ -209,7 +217,8 @@ int main(int argc, char **argv) { } if (error_scan(chld_err.line[i], &is_nxdomain, config.dns_server) != STATE_OK) { - result = max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server)); + result = + max_state(result, error_scan(chld_err.line[i], &is_nxdomain, config.dns_server)); msg = strchr(input_buffer, ':'); if (msg) { msg++; @@ -242,7 +251,8 @@ int main(int argc, char **argv) { } *adrp = 0; } else { - die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), NSLOOKUP_COMMAND); + die(STATE_CRITICAL, _("DNS CRITICAL - '%s' msg parsing exited with no address\n"), + NSLOOKUP_COMMAND); } /* compare to expected address */ @@ -255,7 +265,8 @@ int main(int argc, char **argv) { for (size_t i = 0; i < config.expected_address_cnt; i++) { /* check if we get a match on 'raw' ip or cidr */ for (size_t j = 0; j < n_addresses; j++) { - if (strcmp(addresses[j], config.expected_address[i]) == 0 || ip_match_cidr(addresses[j], config.expected_address[i])) { + if (strcmp(addresses[j], config.expected_address[i]) == 0 || + ip_match_cidr(addresses[j], config.expected_address[i])) { result = STATE_OK; addr_match &= ~(1 << j); expect_match &= ~(1 << i); @@ -279,7 +290,8 @@ int main(int argc, char **argv) { if (config.expect_nxdomain) { if (!is_nxdomain) { result = STATE_CRITICAL; - xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address, address); + xasprintf(&msg, _("Domain '%s' was found by the server: '%s'\n"), config.query_address, + address); } else { if (address != NULL) { free(address); @@ -291,7 +303,8 @@ int main(int argc, char **argv) { /* check if authoritative */ if (result == STATE_OK && config.expect_authority && non_authoritative) { result = STATE_CRITICAL; - xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server, config.query_address); + xasprintf(&msg, _("server %s is not authoritative for %s"), config.dns_server, + config.query_address); } long microsec = deltime(tv); @@ -306,24 +319,36 @@ int main(int argc, char **argv) { } else if (result == STATE_CRITICAL) { printf("DNS %s: ", _("CRITICAL")); } - printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), elapsed_time); + printf(ngettext("%.3f second response time", "%.3f seconds response time", elapsed_time), + elapsed_time); printf(_(". %s returns %s"), config.query_address, address); - if ((config.time_thresholds->warning != NULL) && (config.time_thresholds->critical != NULL)) { - printf("|%s\n", fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, true, + if ((config.time_thresholds->warning != NULL) && + (config.time_thresholds->critical != NULL)) { + printf("|%s\n", + fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, + true, config.time_thresholds->critical->end, true, 0, false, 0)); + } else if ((config.time_thresholds->warning == NULL) && + (config.time_thresholds->critical != NULL)) { + printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true, config.time_thresholds->critical->end, true, 0, false, 0)); - } else if ((config.time_thresholds->warning == NULL) && (config.time_thresholds->critical != NULL)) { - printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, true, config.time_thresholds->critical->end, true, 0, false, 0)); - } else if ((config.time_thresholds->warning != NULL) && (config.time_thresholds->critical == NULL)) { - printf("|%s\n", fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, false, 0, true, 0, false, 0)); + } else if ((config.time_thresholds->warning != NULL) && + (config.time_thresholds->critical == NULL)) { + printf("|%s\n", + fperfdata("time", elapsed_time, "s", true, config.time_thresholds->warning->end, + false, 0, true, 0, false, 0)); } else { - printf("|%s\n", fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); + printf("|%s\n", + fperfdata("time", elapsed_time, "s", false, 0, false, 0, true, 0, false, 0)); } } else if (result == STATE_WARNING) { - printf(_("DNS WARNING - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); + printf(_("DNS WARNING - %s\n"), + !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); } else if (result == STATE_CRITICAL) { - printf(_("DNS CRITICAL - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); + printf(_("DNS CRITICAL - %s\n"), + !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); } else { - printf(_("DNS UNKNOWN - %s\n"), !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); + printf(_("DNS UNKNOWN - %s\n"), + !strcmp(msg, "") ? _(" Probably a non-existent host/domain") : msg); } exit(result); @@ -342,29 +367,34 @@ bool ip_match_cidr(const char *addr, const char *cidr_ro) { mask = atoi(mask_c); /* https://www.cryptobells.com/verifying-ips-in-a-subnet-in-php/ */ - return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) << (32 - mask); + return (ip2long(addr) & ~((1 << (32 - mask)) - 1)) == (ip2long(subnet) >> (32 - mask)) + << (32 - mask); } unsigned long ip2long(const char *src) { unsigned long ip[4]; /* http://computer-programming-forum.com/47-c-language/1376ffb92a12c471.htm */ - return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && - ip[3] < 256) + return (sscanf(src, "%3lu.%3lu.%3lu.%3lu", &ip[0], &ip[1], &ip[2], &ip[3]) == 4 && + ip[0] < 256 && ip[1] < 256 && ip[2] < 256 && ip[3] < 256) ? ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3] : 0; } -mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain, const char dns_server[ADDRESS_LENGTH]) { +mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain, + const char dns_server[ADDRESS_LENGTH]) { - const int nxdomain = strstr(input_buffer, "Non-existent") || strstr(input_buffer, "** server can't find") || + const int nxdomain = strstr(input_buffer, "Non-existent") || + strstr(input_buffer, "** server can't find") || strstr(input_buffer, "** Can't find") || strstr(input_buffer, "NXDOMAIN"); if (nxdomain) { *is_nxdomain = true; } /* the DNS lookup timed out */ - if (strstr(input_buffer, _("Note: nslookup is deprecated and may be removed from future releases.")) || - strstr(input_buffer, _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || + if (strstr(input_buffer, + _("Note: nslookup is deprecated and may be removed from future releases.")) || + strstr(input_buffer, + _("Consider using the `dig' or `host' programs instead. Run nslookup with")) || strstr(input_buffer, _("the `-sil[ent]' option to prevent this message from appearing."))) { return STATE_OK; } @@ -382,8 +412,9 @@ mp_state_enum error_scan(char *input_buffer, bool *is_nxdomain, const char dns_s } /* Connection was refused */ - else if (strstr(input_buffer, "Connection refused") || strstr(input_buffer, "Couldn't find server") || - strstr(input_buffer, "Refused") || (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) { + else if (strstr(input_buffer, "Connection refused") || + strstr(input_buffer, "Couldn't find server") || strstr(input_buffer, "Refused") || + (strstr(input_buffer, "** server can't find") && strstr(input_buffer, ": REFUSED"))) { die(STATE_CRITICAL, _("Connection to DNS %s was refused\n"), dns_server); } @@ -504,20 +535,24 @@ check_dns_config_wrapper process_arguments(int argc, char **argv) { if (strchr(optarg, ',') != NULL) { char *comma = strchr(optarg, ','); while (comma != NULL) { - result.config.expected_address = - (char **)realloc(result.config.expected_address, (result.config.expected_address_cnt + 1) * sizeof(char **)); - result.config.expected_address[result.config.expected_address_cnt] = strndup(optarg, comma - optarg); + result.config.expected_address = (char **)realloc( + result.config.expected_address, + (result.config.expected_address_cnt + 1) * sizeof(char **)); + result.config.expected_address[result.config.expected_address_cnt] = + strndup(optarg, comma - optarg); result.config.expected_address_cnt++; optarg = comma + 1; comma = strchr(optarg, ','); } result.config.expected_address = - (char **)realloc(result.config.expected_address, (result.config.expected_address_cnt + 1) * sizeof(char **)); + (char **)realloc(result.config.expected_address, + (result.config.expected_address_cnt + 1) * sizeof(char **)); result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg); result.config.expected_address_cnt++; } else { result.config.expected_address = - (char **)realloc(result.config.expected_address, (result.config.expected_address_cnt + 1) * sizeof(char **)); + (char **)realloc(result.config.expected_address, + (result.config.expected_address_cnt + 1) * sizeof(char **)); result.config.expected_address[result.config.expected_address_cnt] = strdup(optarg); result.config.expected_address_cnt++; } @@ -586,9 +621,11 @@ void print_help(void) { printf("Copyright (c) 1999 Ethan Galstad \n"); printf(COPYRIGHT, copyright, email); - printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given host/domain query.")); + printf("%s\n", _("This plugin uses the nslookup program to obtain the IP address for the given " + "host/domain query.")); printf("%s\n", _("An optional DNS server to use may be specified.")); - printf("%s\n", _("If no DNS server is specified, the default server(s) specified in /etc/resolv.conf will be used.")); + printf("%s\n", _("If no DNS server is specified, the default server(s) specified in " + "/etc/resolv.conf will be used.")); printf("\n\n"); @@ -602,11 +639,14 @@ void print_help(void) { printf(" -s, --server=HOST\n"); printf(" %s\n", _("Optional DNS server you want to use for the lookup")); printf(" -a, --expected-address=IP-ADDRESS|CIDR|HOST\n"); - printf(" %s\n", _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end")); - printf(" %s\n", _("with a dot (.). This option can be repeated multiple times (Returns OK if any")); + printf(" %s\n", + _("Optional IP-ADDRESS/CIDR you expect the DNS server to return. HOST must end")); + printf(" %s\n", + _("with a dot (.). This option can be repeated multiple times (Returns OK if any")); printf(" %s\n", _("value matches).")); printf(" -n, --expect-nxdomain\n"); - printf(" %s\n", _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)")); + printf(" %s\n", + _("Expect the DNS server to return NXDOMAIN (i.e. the domain was not found)")); printf(" %s\n", _("Cannot be used together with -a")); printf(" -A, --expect-authority\n"); printf(" %s\n", _("Optionally expect the DNS server to be authoritative for the lookup")); @@ -615,7 +655,8 @@ void print_help(void) { printf(" -c, --critical=seconds\n"); printf(" %s\n", _("Return critical if elapsed time exceeds value. Default off")); printf(" -L, --all\n"); - printf(" %s\n", _("Return critical if the list of expected addresses does not match all addresses")); + printf(" %s\n", + _("Return critical if the list of expected addresses does not match all addresses")); printf(" %s\n", _("returned. Default off")); printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); @@ -625,5 +666,7 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c crit] [-L]\n", progname); + printf("%s -H host [-s server] [-a expected-address] [-n] [-A] [-t timeout] [-w warn] [-c " + "crit] [-L]\n", + progname); } diff --git a/plugins/check_dummy.c b/plugins/check_dummy.c index 19f6c046..602d5868 100644 --- a/plugins/check_dummy.c +++ b/plugins/check_dummy.c @@ -45,18 +45,19 @@ int main(int argc, char **argv) { bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - if (argc < 2) + if (argc < 2) { usage4(_("Could not parse arguments")); - else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) { + } else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0) { print_revision(progname, NP_VERSION); exit(STATE_UNKNOWN); } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { print_help(); exit(STATE_UNKNOWN); - } else if (!is_integer(argv[1])) + } else if (!is_integer(argv[1])) { usage4(_("Arguments to check_dummy must be an integer")); - else + } else { result = atoi(argv[1]); + } switch (result) { case STATE_OK: @@ -78,8 +79,9 @@ int main(int argc, char **argv) { return STATE_UNKNOWN; } - if (argc >= 3) + if (argc >= 3) { printf(": %s", argv[2]); + } printf("\n"); @@ -92,7 +94,8 @@ void print_help(void) { printf("Copyright (c) 1999 Ethan Galstad \n"); printf(COPYRIGHT, copyright, email); - printf("%s\n", _("This plugin will simply return the state corresponding to the numeric value")); + printf("%s\n", + _("This plugin will simply return the state corresponding to the numeric value")); printf("%s\n", _("of the argument with optional text")); diff --git a/plugins/check_fping.c b/plugins/check_fping.c index 8018e06d..6160c2cb 100644 --- a/plugins/check_fping.c +++ b/plugins/check_fping.c @@ -46,8 +46,9 @@ enum { RTA = 1 }; -static mp_state_enum textscan(char *buf, const char * /*server_name*/, bool /*crta_p*/, double /*crta*/, bool /*wrta_p*/, double /*wrta*/, - bool /*cpl_p*/, int /*cpl*/, bool /*wpl_p*/, int /*wpl*/, bool /*alive_p*/); +static mp_state_enum textscan(char *buf, const char * /*server_name*/, bool /*crta_p*/, + double /*crta*/, bool /*wrta_p*/, double /*wrta*/, bool /*cpl_p*/, + int /*cpl*/, bool /*wpl_p*/, int /*wpl*/, bool /*alive_p*/); typedef struct { int errorcode; @@ -133,9 +134,11 @@ int main(int argc, char **argv) { if (config.icmp_timestamp) { // no packet size settable for ICMP timestamp - xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count, server); + xasprintf(&command_line, "%s %s -c %d %s", fping_prog, option_string, config.packet_count, + server); } else { - xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, config.packet_size, config.packet_count, server); + xasprintf(&command_line, "%s %s-b %d -c %d %s", fping_prog, option_string, + config.packet_size, config.packet_count, server); } if (verbose) { @@ -160,8 +163,9 @@ int main(int argc, char **argv) { if (verbose) { printf("%s", input_buffer); } - status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p, config.crta, config.wrta_p, config.wrta, - config.cpl_p, config.cpl, config.wpl_p, config.wpl, config.alive_p)); + status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p, + config.crta, config.wrta_p, config.wrta, config.cpl_p, + config.cpl, config.wpl_p, config.wpl, config.alive_p)); } /* If we get anything on STDERR, at least set warning */ @@ -170,8 +174,9 @@ int main(int argc, char **argv) { if (verbose) { printf("%s", input_buffer); } - status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p, config.crta, config.wrta_p, config.wrta, - config.cpl_p, config.cpl, config.wpl_p, config.wpl, config.alive_p)); + status = max_state(status, textscan(input_buffer, config.server_name, config.crta_p, + config.crta, config.wrta_p, config.wrta, config.cpl_p, + config.cpl, config.wpl_p, config.wpl, config.alive_p)); } (void)fclose(child_stderr); @@ -200,8 +205,8 @@ int main(int argc, char **argv) { return status; } -mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double crta, bool wrta_p, double wrta, bool cpl_p, int cpl, - bool wpl_p, int wpl, bool alive_p) { +mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double crta, bool wrta_p, + double wrta, bool cpl_p, int cpl, bool wpl_p, int wpl, bool alive_p) { /* stops testing after the first successful reply. */ double rta; double loss; @@ -214,7 +219,8 @@ mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double c die(STATE_OK, _("FPING %s - %s (rta=%f ms)|%s\n"), state_text(STATE_OK), server_name, rta, /* No loss since we only waited for the first reply perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, true, 0, true, 100), */ - fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); + fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, + false, 0)); } mp_state_enum status = STATE_UNKNOWN; @@ -255,9 +261,11 @@ mp_state_enum textscan(char *buf, const char *server_name, bool crta_p, double c } else { status = STATE_OK; } - die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status), server_name, loss, rta, + die(status, _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"), state_text(status), + server_name, loss, rta, perfdata("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, false, 0, false, 0), - fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, false, 0)); + fperfdata("rta", rta / 1.0e3, "s", wrta_p, wrta / 1.0e3, crta_p, crta / 1.0e3, true, 0, + false, 0)); } else if (strstr(buf, "xmt/rcv/%loss")) { /* no min/max/avg if host was unreachable in fping v2.2.b1 */ @@ -350,7 +358,8 @@ check_fping_config_wrapper process_arguments(int argc, char **argv) { } while (true) { - int option_index = getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); + int option_index = + getopt_long(argc, argv, "+hVvaH:S:c:w:b:n:T:i:I:M:R:46", longopts, &option); if (option_index == -1 || option_index == EOF || option_index == 1) { break; @@ -487,10 +496,12 @@ int get_threshold(char *arg, char *rv[2]) { if (arg2) { arg1[strcspn(arg1, ",:")] = 0; if (strstr(arg1, "%") && strstr(arg2, "%")) { - die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname, arg); + die(STATE_UNKNOWN, _("%s: Only one threshold may be packet loss (%s)\n"), progname, + arg); } if (!strstr(arg1, "%") && !strstr(arg2, "%")) { - die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname, arg); + die(STATE_UNKNOWN, _("%s: Only one threshold must be packet loss (%s)\n"), progname, + arg); } } @@ -516,7 +527,8 @@ void print_help(void) { printf("Copyright (c) 1999 Didi Rieder \n"); printf(COPYRIGHT, copyright, email); - printf("%s\n", _("This plugin will use the fping command to ping the specified host for a fast check")); + printf("%s\n", + _("This plugin will use the fping command to ping the specified host for a fast check")); printf("%s\n", _("Note that it is necessary to set the suid flag on fping.")); @@ -530,7 +542,8 @@ void print_help(void) { printf(UT_IPv46); printf(" %s\n", "-H, --hostname=HOST"); - printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, reducing system load)")); + printf(" %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, " + "reducing system load)")); printf(" %s\n", "-w, --warning=THRESHOLD"); printf(" %s\n", _("warning threshold pair")); printf(" %s\n", "-c, --critical=THRESHOLD"); @@ -544,7 +557,8 @@ void print_help(void) { printf(" %s\n", "-T, --target-timeout=INTEGER"); printf(" %s (default: fping's default for -t)\n", _("Target timeout (ms)")); printf(" %s\n", "-i, --interval=INTEGER"); - printf(" %s (default: fping's default for -p)\n", _("Interval (ms) between sending packets")); + printf(" %s (default: fping's default for -p)\n", + _("Interval (ms) between sending packets")); printf(" %s\n", "-S, --sourceip=HOST"); printf(" %s\n", _("name or IP Address of sourceip")); printf(" %s\n", "-I, --sourceif=IF"); @@ -565,7 +579,8 @@ void print_help(void) { #endif printf(UT_VERBOSE); printf("\n"); - printf(" %s\n", _("THRESHOLD is ,%% where is the round trip average travel time (ms)")); + printf(" %s\n", + _("THRESHOLD is ,%% where is the round trip average travel time (ms)")); printf(" %s\n", _("which triggers a WARNING or CRITICAL state, and is the percentage of")); printf(" %s\n", _("packet loss to trigger an alarm state.")); @@ -577,5 +592,6 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf(" %s -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", progname); + printf(" %s -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", + progname); } diff --git a/plugins/check_fping.d/config.h b/plugins/check_fping.d/config.h index d95e9ded..d3e50565 100644 --- a/plugins/check_fping.d/config.h +++ b/plugins/check_fping.d/config.h @@ -36,7 +36,6 @@ typedef struct { unsigned int fwmark; bool fwmark_set; - // only available with fping version >= 5.3 // Setting icmp_timestamp tells fping to use ICMP Timestamp (ICMP type 13) instead // of ICMP Echo diff --git a/plugins/check_game.c b/plugins/check_game.c index c0193b03..974a7253 100644 --- a/plugins/check_game.c +++ b/plugins/check_game.c @@ -77,7 +77,8 @@ int main(int argc, char **argv) { /* create the command line to execute */ char *command_line = NULL; - xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, config.game_type, config.server_ip); + xasprintf(&command_line, "%s -raw %s -%s %s", PATH_TO_QSTAT, QSTAT_DATA_DELIMITER, + config.game_type, config.server_ip); if (config.port) { xasprintf(&command_line, "%s:%-d", command_line, config.port); @@ -130,11 +131,13 @@ int main(int argc, char **argv) { printf(_("CRITICAL - Game server timeout\n")); result = STATE_CRITICAL; } else { - printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players], ret[config.qstat_game_players_max], - ret[config.qstat_game_field], ret[config.qstat_map_field], ret[config.qstat_ping_field], - perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0, true, 0, true, - atol(ret[config.qstat_game_players_max])), - fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0, true, 0, false, 0)); + printf("OK: %s/%s %s (%s), Ping: %s ms|%s %s\n", ret[config.qstat_game_players], + ret[config.qstat_game_players_max], ret[config.qstat_game_field], + ret[config.qstat_map_field], ret[config.qstat_ping_field], + perfdata("players", atol(ret[config.qstat_game_players]), "", false, 0, false, 0, + true, 0, true, atol(ret[config.qstat_game_players_max])), + fperfdata("ping", strtod(ret[config.qstat_ping_field], NULL), "", false, 0, false, 0, + true, 0, false, 0)); } exit(result); @@ -144,19 +147,20 @@ int main(int argc, char **argv) { #define max_players_field_index 130 check_game_config_wrapper process_arguments(int argc, char **argv) { - static struct option long_opts[] = {{"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'V'}, - {"verbose", no_argument, 0, 'v'}, - {"timeout", required_argument, 0, 't'}, - {"hostname", required_argument, 0, 'H'}, - {"port", required_argument, 0, 'P'}, - {"game-type", required_argument, 0, 'G'}, - {"map-field", required_argument, 0, 'm'}, - {"ping-field", required_argument, 0, 'p'}, - {"game-field", required_argument, 0, 'g'}, - {"players-field", required_argument, 0, players_field_index}, - {"max-players-field", required_argument, 0, max_players_field_index}, - {0, 0, 0, 0}}; + static struct option long_opts[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"verbose", no_argument, 0, 'v'}, + {"timeout", required_argument, 0, 't'}, + {"hostname", required_argument, 0, 'H'}, + {"port", required_argument, 0, 'P'}, + {"game-type", required_argument, 0, 'G'}, + {"map-field", required_argument, 0, 'm'}, + {"ping-field", required_argument, 0, 'p'}, + {"game-field", required_argument, 0, 'g'}, + {"players-field", required_argument, 0, players_field_index}, + {"max-players-field", required_argument, 0, max_players_field_index}, + {0, 0, 0, 0}}; check_game_config_wrapper result = { .config = check_game_config_init(), @@ -216,21 +220,24 @@ check_game_config_wrapper process_arguments(int argc, char **argv) { break; case 'p': /* index of ping field */ result.config.qstat_ping_field = atoi(optarg); - if (result.config.qstat_ping_field < 0 || result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) { + if (result.config.qstat_ping_field < 0 || + result.config.qstat_ping_field > QSTAT_MAX_RETURN_ARGS) { result.errorcode = ERROR; return result; } break; case 'm': /* index on map field */ result.config.qstat_map_field = atoi(optarg); - if (result.config.qstat_map_field < 0 || result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) { + if (result.config.qstat_map_field < 0 || + result.config.qstat_map_field > QSTAT_MAX_RETURN_ARGS) { result.errorcode = ERROR; return result; } break; case 'g': /* index of game field */ result.config.qstat_game_field = atoi(optarg); - if (result.config.qstat_game_field < 0 || result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) { + if (result.config.qstat_game_field < 0 || + result.config.qstat_game_field > QSTAT_MAX_RETURN_ARGS) { result.errorcode = ERROR; return result; } @@ -240,14 +247,16 @@ check_game_config_wrapper process_arguments(int argc, char **argv) { if (result.config.qstat_game_players_max == 0) { result.config.qstat_game_players_max = result.config.qstat_game_players - 1; } - if (result.config.qstat_game_players < 0 || result.config.qstat_game_players > QSTAT_MAX_RETURN_ARGS) { + if (result.config.qstat_game_players < 0 || + result.config.qstat_game_players > QSTAT_MAX_RETURN_ARGS) { result.errorcode = ERROR; return result; } break; case max_players_field_index: /* index of max players field */ result.config.qstat_game_players_max = atoi(optarg); - if (result.config.qstat_game_players_max < 0 || result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) { + if (result.config.qstat_game_players_max < 0 || + result.config.qstat_game_players_max > QSTAT_MAX_RETURN_ARGS) { result.errorcode = ERROR; return result; } @@ -286,7 +295,7 @@ void print_help(void) { printf(UT_HELP_VRSN); printf(UT_EXTRA_OPTS); printf(" -H, --hostname=ADDRESS\n" - " Host name, IP Address, or unix socket (must be an absolute path)\n"); + " Host name, IP Address, or unix socket (must be an absolute path)\n"); printf(" %s\n", "-P"); printf(" %s\n", _("Optional port to connect to")); printf(" %s\n", "-g"); @@ -300,8 +309,10 @@ void print_help(void) { printf("\n"); printf("%s\n", _("Notes:")); - printf(" %s\n", _("This plugin uses the 'qstat' command, the popular game server status query tool.")); - printf(" %s\n", _("If you don't have the package installed, you will need to download it from")); + printf(" %s\n", + _("This plugin uses the 'qstat' command, the popular game server status query tool.")); + printf(" %s\n", + _("If you don't have the package installed, you will need to download it from")); printf(" %s\n", _("https://github.com/multiplay/qstat before you can use this plugin.")); printf(UT_SUPPORT); @@ -309,7 +320,8 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G game-time] [-H hostname] " + printf(" %s [-hvV] [-P port] [-t timeout] [-g game_field] [-m map_field] [-p ping_field] [-G " + "game-time] [-H hostname] " "\n", progname); } diff --git a/plugins/check_hpjd.c b/plugins/check_hpjd.c index 62417fd6..9907abc5 100644 --- a/plugins/check_hpjd.c +++ b/plugins/check_hpjd.c @@ -85,13 +85,16 @@ int main(int argc, char **argv) { char query_string[512]; /* removed ' 2>1' at end of command 10/27/1999 - EG */ /* create the query string */ - sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0", HPJD_LINE_STATUS, HPJD_PAPER_STATUS, - HPJD_INTERVENTION_REQUIRED, HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW, - HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, HPJD_GD_STATUS_DISPLAY); + sprintf(query_string, "%s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0 %s.0", + HPJD_LINE_STATUS, HPJD_PAPER_STATUS, HPJD_INTERVENTION_REQUIRED, + HPJD_GD_PERIPHERAL_ERROR, HPJD_GD_PAPER_JAM, HPJD_GD_PAPER_OUT, HPJD_GD_TONER_LOW, + HPJD_GD_PAGE_PUNT, HPJD_GD_MEMORY_OUT, HPJD_GD_DOOR_OPEN, HPJD_GD_PAPER_OUTPUT, + HPJD_GD_STATUS_DISPLAY); /* get the command to run */ char command_line[1024]; - sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, config.community, config.address, config.port, query_string); + sprintf(command_line, "%s -OQa -m : -v 1 -c %s %s:%u %s", PATH_TO_SNMPGET, config.community, + config.address, config.port, query_string); /* run the command */ child_process = spopen(command_line); @@ -177,7 +180,8 @@ int main(int argc, char **argv) { strcpy(display_message, temp_buffer + 1); break; default: /* fold multiline message */ - strncat(display_message, input_buffer, sizeof(display_message) - strlen(display_message) - 1); + strncat(display_message, input_buffer, + sizeof(display_message) - strlen(display_message) - 1); } } diff --git a/plugins/check_ldap.c b/plugins/check_ldap.c index 597644bd..77a33304 100644 --- a/plugins/check_ldap.c +++ b/plugins/check_ldap.c @@ -108,7 +108,8 @@ int main(int argc, char *argv[]) { #ifdef HAVE_LDAP_SET_OPTION /* set ldap options */ - if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) != LDAP_OPT_SUCCESS) { + if (ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &config.ld_protocol) != + LDAP_OPT_SUCCESS) { printf(_("Could not set protocol version %d\n"), config.ld_protocol); return STATE_CRITICAL; } @@ -135,7 +136,8 @@ int main(int argc, char *argv[]) { } else if (config.starttls) { #if defined(HAVE_LDAP_SET_OPTION) && defined(HAVE_LDAP_START_TLS_S) /* ldap with startTLS: set option version */ - if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) == LDAP_OPT_SUCCESS) { + if (ldap_get_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version) == + LDAP_OPT_SUCCESS) { if (version < LDAP_VERSION3) { version = LDAP_VERSION3; ldap_set_option(ldap_connection, LDAP_OPT_PROTOCOL_VERSION, &version); @@ -156,7 +158,8 @@ int main(int argc, char *argv[]) { } /* bind to the ldap server */ - if (ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE) != LDAP_SUCCESS) { + if (ldap_bind_s(ldap_connection, config.ld_binddn, config.ld_passwd, LDAP_AUTH_SIMPLE) != + LDAP_SUCCESS) { if (verbose) { ldap_perror(ldap_connection, "ldap_bind"); } @@ -168,8 +171,10 @@ int main(int argc, char *argv[]) { int num_entries = 0; /* do a search of all objectclasses in the base dn */ if (ldap_search_s(ldap_connection, config.ld_base, - (config.crit_entries != NULL || config.warn_entries != NULL) ? LDAP_SCOPE_SUBTREE : LDAP_SCOPE_BASE, config.ld_attr, - NULL, 0, &result) != LDAP_SUCCESS) { + (config.crit_entries != NULL || config.warn_entries != NULL) + ? LDAP_SCOPE_SUBTREE + : LDAP_SCOPE_BASE, + config.ld_attr, NULL, 0, &result) != LDAP_SUCCESS) { if (verbose) { ldap_perror(ldap_connection, "ldap_search"); } @@ -215,14 +220,16 @@ int main(int argc, char *argv[]) { /* print out the result */ if (config.crit_entries != NULL || config.warn_entries != NULL) { - printf(_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), state_text(status), num_entries, elapsed_time, - fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time, config.crit_time_set, config.crit_time, true, 0, - false, 0), - sperfdata("entries", (double)num_entries, "", config.warn_entries, config.crit_entries, true, 0.0, false, 0.0)); + printf(_("LDAP %s - found %d entries in %.3f seconds|%s %s\n"), state_text(status), + num_entries, elapsed_time, + fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time, + config.crit_time_set, config.crit_time, true, 0, false, 0), + sperfdata("entries", (double)num_entries, "", config.warn_entries, + config.crit_entries, true, 0.0, false, 0.0)); } else { printf(_("LDAP %s - %.3f seconds response time|%s\n"), state_text(status), elapsed_time, - fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time, config.crit_time_set, config.crit_time, true, 0, - false, 0)); + fperfdata("time", elapsed_time, "s", config.warn_time_set, config.warn_time, + config.crit_time_set, config.crit_time, true, 0, false, 0)); } exit(status); @@ -273,7 +280,8 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) { int option = 0; while (true) { - int option_index = getopt_long(argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option); + int option_index = + getopt_long(argc, argv, "hvV234TS6t:c:w:H:b:p:a:D:P:C:W:", longopts, &option); if (option_index == -1 || option_index == EOF) { break; @@ -381,7 +389,8 @@ check_ldap_config_wrapper process_arguments(int argc, char **argv) { result.config.ld_port = DEFAULT_PORT; } - if (strstr(argv[0], "check_ldaps") && !result.config.starttls && !result.config.ssl_on_connect) { + if (strstr(argv[0], "check_ldaps") && !result.config.starttls && + !result.config.ssl_on_connect) { result.config.starttls = true; } @@ -398,7 +407,8 @@ check_ldap_config_wrapper validate_arguments(check_ldap_config_wrapper config_wr } if (config_wrapper.config.crit_entries != NULL || config_wrapper.config.warn_entries != NULL) { - set_thresholds(&config_wrapper.config.entries_thresholds, config_wrapper.config.warn_entries, config_wrapper.config.crit_entries); + set_thresholds(&config_wrapper.config.entries_thresholds, + config_wrapper.config.warn_entries, config_wrapper.config.crit_entries); } if (config_wrapper.config.ld_passwd == NULL) { @@ -435,11 +445,13 @@ void print_help(void) { printf(" %s\n", "-D [--bind]"); printf(" %s\n", _("ldap bind DN (if required)")); printf(" %s\n", "-P [--pass]"); - printf(" %s\n", _("ldap password (if required, or set the password through environment variable 'LDAP_PASSWORD')")); + printf(" %s\n", _("ldap password (if required, or set the password through environment " + "variable 'LDAP_PASSWORD')")); printf(" %s\n", "-T [--starttls]"); printf(" %s\n", _("use starttls mechanism introduced in protocol version 3")); printf(" %s\n", "-S [--ssl]"); - printf(" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"), LDAPS_PORT); + printf(" %s %i\n", _("use ldaps (ldap v2 ssl method). this also sets the default port to"), + LDAPS_PORT); #ifdef HAVE_LDAP_SET_OPTION printf(" %s\n", "-2 [--ver2]"); @@ -463,9 +475,11 @@ void print_help(void) { printf("\n"); printf("%s\n", _("Notes:")); printf(" %s\n", _("If this plugin is called via 'check_ldaps', method 'STARTTLS' will be")); - printf(_(" implied (using default port %i) unless --port=636 is specified. In that case\n"), DEFAULT_PORT); + printf(_(" implied (using default port %i) unless --port=636 is specified. In that case\n"), + DEFAULT_PORT); printf(" %s\n", _("'SSL on connect' will be used no matter how the plugin was called.")); - printf(" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' or '--ssl' flags")); + printf(" %s\n", _("This detection is deprecated, please use 'check_ldap' with the '--starttls' " + "or '--ssl' flags")); printf(" %s\n", _("to define the behaviour explicitly instead.")); printf(" %s\n", _("The parameters --warn-entries and --crit-entries are optional.")); diff --git a/plugins/check_load.c b/plugins/check_load.c index 2925bff3..f7a6f7fd 100644 --- a/plugins/check_load.c +++ b/plugins/check_load.c @@ -168,7 +168,8 @@ int main(int argc, char **argv) { mp_subcheck scaled_load_sc1 = mp_subcheck_init(); scaled_load_sc1 = mp_set_subcheck_state(scaled_load_sc1, mp_get_pd_status(pd_scaled_load1)); mp_add_perfdata_to_subcheck(&scaled_load_sc1, pd_scaled_load1); - xasprintf(&scaled_load_sc1.output, "1 Minute: %s", pd_value_to_string(pd_scaled_load1.value)); + xasprintf(&scaled_load_sc1.output, "1 Minute: %s", + pd_value_to_string(pd_scaled_load1.value)); mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc1); mp_perfdata pd_scaled_load5 = perfdata_init(); @@ -179,7 +180,8 @@ int main(int argc, char **argv) { mp_subcheck scaled_load_sc5 = mp_subcheck_init(); scaled_load_sc5 = mp_set_subcheck_state(scaled_load_sc5, mp_get_pd_status(pd_scaled_load5)); mp_add_perfdata_to_subcheck(&scaled_load_sc5, pd_scaled_load5); - xasprintf(&scaled_load_sc5.output, "5 Minutes: %s", pd_value_to_string(pd_scaled_load5.value)); + xasprintf(&scaled_load_sc5.output, "5 Minutes: %s", + pd_value_to_string(pd_scaled_load5.value)); mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc5); mp_perfdata pd_scaled_load15 = perfdata_init(); @@ -188,9 +190,11 @@ int main(int argc, char **argv) { pd_scaled_load15 = mp_pd_set_thresholds(pd_scaled_load15, config.th_load[2]); mp_subcheck scaled_load_sc15 = mp_subcheck_init(); - scaled_load_sc15 = mp_set_subcheck_state(scaled_load_sc15, mp_get_pd_status(pd_scaled_load15)); + scaled_load_sc15 = + mp_set_subcheck_state(scaled_load_sc15, mp_get_pd_status(pd_scaled_load15)); mp_add_perfdata_to_subcheck(&scaled_load_sc15, pd_scaled_load15); - xasprintf(&scaled_load_sc15.output, "15 Minutes: %s", pd_value_to_string(pd_scaled_load15.value)); + xasprintf(&scaled_load_sc15.output, "15 Minutes: %s", + pd_value_to_string(pd_scaled_load15.value)); mp_add_subcheck_to_subcheck(&scaled_load_sc, scaled_load_sc15); mp_add_subcheck_to_check(&overall, scaled_load_sc); @@ -245,11 +249,13 @@ int main(int argc, char **argv) { mp_subcheck top_proc_sc = mp_subcheck_init(); top_proc_sc = mp_set_subcheck_state(top_proc_sc, STATE_OK); top_processes_result top_proc = print_top_consuming_processes(config.n_procs_to_show); - xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes", config.n_procs_to_show); + xasprintf(&top_proc_sc.output, "Top %lu CPU time consuming processes", + config.n_procs_to_show); if (top_proc.errorcode == OK) { for (unsigned long i = 0; i < config.n_procs_to_show; i++) { - xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output, top_proc.top_processes[i]); + xasprintf(&top_proc_sc.output, "%s\n%s", top_proc_sc.output, + top_proc.top_processes[i]); } } @@ -417,7 +423,8 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", progname); + printf("%s [-r] -w WLOAD1,WLOAD5,WLOAD15 -c CLOAD1,CLOAD5,CLOAD15 [-n NUMBER_OF_PROCS]\n", + progname); } #ifdef PS_USES_PROCPCPU @@ -462,7 +469,8 @@ static top_processes_result print_top_consuming_processes(unsigned long n_procs_ #ifdef PS_USES_PROCPCPU qsort(chld_out.line + 1, chld_out.lines - 1, sizeof(char *), cmpstringp); #endif /* PS_USES_PROCPCPU */ - unsigned long lines_to_show = chld_out.lines < (size_t)(n_procs_to_show + 1) ? chld_out.lines : n_procs_to_show + 1; + unsigned long lines_to_show = + chld_out.lines < (size_t)(n_procs_to_show + 1) ? chld_out.lines : n_procs_to_show + 1; result.top_processes = calloc(lines_to_show, sizeof(char *)); if (result.top_processes == NULL) { diff --git a/plugins/check_mrtg.c b/plugins/check_mrtg.c index 5bd276dc..4a17049a 100644 --- a/plugins/check_mrtg.c +++ b/plugins/check_mrtg.c @@ -129,7 +129,8 @@ int main(int argc, char **argv) { time_t current_time; time(¤t_time); if (config.expire_minutes > 0 && (current_time - timestamp) > (config.expire_minutes * 60)) { - printf(_("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); + printf(_("MRTG data has expired (%d minutes old)\n"), + (int)((current_time - timestamp) / 60)); return STATE_WARNING; } @@ -148,20 +149,29 @@ int main(int argc, char **argv) { result = STATE_WARNING; } - printf("%s. %s = %lu %s|%s\n", (config.use_average) ? _("Avg") : _("Max"), config.label, rate, config.units, - perfdata(config.label, (long)rate, config.units, config.value_warning_threshold_set, (long)config.value_warning_threshold, - config.value_critical_threshold_set, (long)config.value_critical_threshold, 0, 0, 0, 0)); + printf("%s. %s = %lu %s|%s\n", (config.use_average) ? _("Avg") : _("Max"), config.label, rate, + config.units, + perfdata(config.label, (long)rate, config.units, config.value_warning_threshold_set, + (long)config.value_warning_threshold, config.value_critical_threshold_set, + (long)config.value_critical_threshold, 0, 0, 0, 0)); return result; } /* process command-line arguments */ check_mrtg_config_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = { - {"logfile", required_argument, 0, 'F'}, {"expires", required_argument, 0, 'e'}, {"aggregation", required_argument, 0, 'a'}, - {"variable", required_argument, 0, 'v'}, {"critical", required_argument, 0, 'c'}, {"warning", required_argument, 0, 'w'}, - {"label", required_argument, 0, 'l'}, {"units", required_argument, 0, 'u'}, {"variable", required_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; + static struct option longopts[] = {{"logfile", required_argument, 0, 'F'}, + {"expires", required_argument, 0, 'e'}, + {"aggregation", required_argument, 0, 'a'}, + {"variable", required_argument, 0, 'v'}, + {"critical", required_argument, 0, 'c'}, + {"warning", required_argument, 0, 'w'}, + {"label", required_argument, 0, 'l'}, + {"units", required_argument, 0, 'u'}, + {"variable", required_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; check_mrtg_config_wrapper result = { .errorcode = OK, @@ -242,7 +252,9 @@ check_mrtg_config_wrapper process_arguments(int argc, char **argv) { if (is_intpos(argv[option_char])) { result.config.expire_minutes = atoi(argv[option_char++]); } else { - die(STATE_UNKNOWN, _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), argv[option_char], progname); + die(STATE_UNKNOWN, + _("%s is not a valid expiration time\nUse '%s -h' for additional help\n"), + argv[option_char], progname); } } @@ -334,25 +346,32 @@ void print_help(void) { printf(" %s\n", _("\"Bytes Per Second\", \"%% Utilization\")")); printf("\n"); - printf(" %s\n", _("If the value exceeds the threshold, a WARNING status is returned. If")); + printf(" %s\n", + _("If the value exceeds the threshold, a WARNING status is returned. If")); printf(" %s\n", _("the value exceeds the threshold, a CRITICAL status is returned. If")); printf(" %s\n", _("the data in the log file is older than old, a WARNING")); printf(" %s\n", _("status is returned and a warning message is printed.")); printf("\n"); - printf(" %s\n", _("This plugin is useful for monitoring MRTG data that does not correspond to")); - printf(" %s\n", _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); - printf(" %s\n", _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); - printf(" %s\n", _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); + printf(" %s\n", + _("This plugin is useful for monitoring MRTG data that does not correspond to")); + printf(" %s\n", + _("bandwidth usage. (Use the check_mrtgtraf plugin for monitoring bandwidth).")); + printf(" %s\n", + _("It can be used to monitor any kind of data that MRTG is monitoring - errors,")); + printf(" %s\n", + _("packets/sec, etc. I use MRTG in conjunction with the Novell NLM that allows")); printf(" %s\n", _("me to track processor utilization, user connections, drive space, etc and")); printf(" %s\n\n", _("this plugin works well for monitoring that kind of data as well.")); printf("%s\n", _("Notes:")); - printf(" %s\n", _("- This plugin only monitors one of the two variables stored in the MRTG log")); + printf(" %s\n", + _("- This plugin only monitors one of the two variables stored in the MRTG log")); printf(" %s\n", _("file. If you want to monitor both values you will have to define two")); printf(" %s\n", _("commands with different values for the argument. Of course,")); printf(" %s\n", _("you can always hack the code to make this plugin work for you...")); - printf(" %s\n", _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); + printf(" %s\n", + _("- MRTG stands for the Multi Router Traffic Grapher. It can be downloaded from")); printf(" %s\n", "http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html"); printf(UT_SUPPORT); diff --git a/plugins/check_mrtgtraf.c b/plugins/check_mrtgtraf.c index 8c7cf8aa..10ce936f 100644 --- a/plugins/check_mrtgtraf.c +++ b/plugins/check_mrtgtraf.c @@ -122,7 +122,8 @@ int main(int argc, char **argv) { time_t current_time; time(¤t_time); if ((config.expire_minutes > 0) && (current_time - timestamp) > (config.expire_minutes * 60)) { - die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"), (int)((current_time - timestamp) / 60)); + die(STATE_WARNING, _("MRTG data has expired (%d minutes old)\n"), + (int)((current_time - timestamp) / 60)); } unsigned long incoming_rate = 0L; @@ -177,21 +178,26 @@ int main(int argc, char **argv) { } int result = STATE_OK; - if (incoming_rate > config.incoming_critical_threshold || outgoing_rate > config.outgoing_critical_threshold) { + if (incoming_rate > config.incoming_critical_threshold || + outgoing_rate > config.outgoing_critical_threshold) { result = STATE_CRITICAL; - } else if (incoming_rate > config.incoming_warning_threshold || outgoing_rate > config.outgoing_warning_threshold) { + } else if (incoming_rate > config.incoming_warning_threshold || + outgoing_rate > config.outgoing_warning_threshold) { result = STATE_WARNING; } char *error_message; - xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"), (config.use_average) ? _("Avg") : _("Max"), - adjusted_incoming_rate, incoming_speed_rating, (config.use_average) ? _("Avg") : _("Max"), adjusted_outgoing_rate, - outgoing_speed_rating, - fperfdata("in", adjusted_incoming_rate, incoming_speed_rating, (int)config.incoming_warning_threshold, - config.incoming_warning_threshold, (int)config.incoming_critical_threshold, config.incoming_critical_threshold, + xasprintf(&error_message, _("%s. In = %0.1f %s/s, %s. Out = %0.1f %s/s|%s %s\n"), + (config.use_average) ? _("Avg") : _("Max"), adjusted_incoming_rate, + incoming_speed_rating, (config.use_average) ? _("Avg") : _("Max"), + adjusted_outgoing_rate, outgoing_speed_rating, + fperfdata("in", adjusted_incoming_rate, incoming_speed_rating, + (int)config.incoming_warning_threshold, config.incoming_warning_threshold, + (int)config.incoming_critical_threshold, config.incoming_critical_threshold, true, 0, false, 0), - fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating, (int)config.outgoing_warning_threshold, - config.outgoing_warning_threshold, (int)config.outgoing_critical_threshold, config.outgoing_critical_threshold, + fperfdata("out", adjusted_outgoing_rate, outgoing_speed_rating, + (int)config.outgoing_warning_threshold, config.outgoing_warning_threshold, + (int)config.outgoing_critical_threshold, config.outgoing_critical_threshold, true, 0, false, 0)); printf(_("Traffic %s - %s\n"), state_text(result), error_message); @@ -249,10 +255,12 @@ check_mrtgtraf_config_wrapper process_arguments(int argc, char **argv) { result.config.use_average = (bool)(strcmp(optarg, "MAX")); break; case 'c': /* warning threshold */ - sscanf(optarg, "%lu,%lu", &result.config.incoming_critical_threshold, &result.config.outgoing_critical_threshold); + sscanf(optarg, "%lu,%lu", &result.config.incoming_critical_threshold, + &result.config.outgoing_critical_threshold); break; case 'w': /* critical threshold */ - sscanf(optarg, "%lu,%lu", &result.config.incoming_warning_threshold, &result.config.outgoing_warning_threshold); + sscanf(optarg, "%lu,%lu", &result.config.incoming_warning_threshold, + &result.config.outgoing_warning_threshold); break; case 'V': /* version */ print_revision(progname, NP_VERSION); diff --git a/plugins/check_mysql.c b/plugins/check_mysql.c index ca3422b5..3d7ec4cd 100644 --- a/plugins/check_mysql.c +++ b/plugins/check_mysql.c @@ -50,15 +50,23 @@ static int verbose = 0; #define LENGTH_METRIC_UNIT 6 static const char *metric_unit[LENGTH_METRIC_UNIT] = { - "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", "Threads_connected", "Threads_running"}; + "Open_files", "Open_tables", "Qcache_free_memory", "Qcache_queries_in_cache", + "Threads_connected", "Threads_running"}; #define LENGTH_METRIC_COUNTER 9 -static const char *metric_counter[LENGTH_METRIC_COUNTER] = { - "Connections", "Qcache_hits", "Qcache_inserts", "Qcache_lowmem_prunes", "Qcache_not_cached", "Queries", - "Questions", "Table_locks_waited", "Uptime"}; - -#define MYSQLDUMP_THREADS_QUERY \ - "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE 'SELECT /*!40001 SQL_NO_CACHE */%'" +static const char *metric_counter[LENGTH_METRIC_COUNTER] = {"Connections", + "Qcache_hits", + "Qcache_inserts", + "Qcache_lowmem_prunes", + "Qcache_not_cached", + "Queries", + "Questions", + "Table_locks_waited", + "Uptime"}; + +#define MYSQLDUMP_THREADS_QUERY \ + "SELECT COUNT(1) mysqldumpThreads FROM information_schema.processlist WHERE info LIKE " \ + "'SELECT /*!40001 SQL_NO_CACHE */%'" typedef struct { int errorcode; @@ -99,16 +107,19 @@ int main(int argc, char **argv) { } if (config.ssl) { - mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir, config.ciphers); + mysql_ssl_set(&mysql, config.key, config.cert, config.ca_cert, config.ca_dir, + config.ciphers); } /* establish a connection to the server and error checking */ - if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, config.db_port, config.db_socket, 0)) { + if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, + config.db_port, config.db_socket, 0)) { /* Depending on internally-selected auth plugin MySQL might return */ /* ER_ACCESS_DENIED_NO_PASSWORD_ERROR or ER_ACCESS_DENIED_ERROR. */ /* Semantically these errors are the same. */ - if (config.ignore_auth && - (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR || mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) { - printf("MySQL OK - Version: %s (protocol %d)\n", mysql_get_server_info(&mysql), mysql_get_proto_info(&mysql)); + if (config.ignore_auth && (mysql_errno(&mysql) == ER_ACCESS_DENIED_ERROR || + mysql_errno(&mysql) == ER_ACCESS_DENIED_NO_PASSWORD_ERROR)) { + printf("MySQL OK - Version: %s (protocol %d)\n", mysql_get_server_info(&mysql), + mysql_get_proto_info(&mysql)); mysql_close(&mysql); return STATE_OK; } @@ -157,13 +168,17 @@ int main(int argc, char **argv) { while ((row = mysql_fetch_row(res)) != NULL) { for (int i = 0; i < LENGTH_METRIC_UNIT; i++) { if (strcmp(row[0], metric_unit[i]) == 0) { - xasprintf(&perf, "%s%s ", perf, perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false, 0, false, 0)); + xasprintf(&perf, "%s%s ", perf, + perfdata(metric_unit[i], atol(row[1]), "", false, 0, false, 0, false, + 0, false, 0)); continue; } } for (int i = 0; i < LENGTH_METRIC_COUNTER; i++) { if (strcmp(row[0], metric_counter[i]) == 0) { - xasprintf(&perf, "%s%s ", perf, perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0, false, 0, false, 0)); + xasprintf(&perf, "%s%s ", perf, + perfdata(metric_counter[i], atol(row[1]), "c", false, 0, false, 0, + false, 0, false, 0)); continue; } } @@ -189,8 +204,8 @@ int main(int argc, char **argv) { unsigned long minor_version = (server_verion_int % 10000) / 100; unsigned long patch_version = (server_verion_int % 100); if (verbose) { - printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n", server_version, major_version, - minor_version, patch_version); + printf("Found MariaDB: %s, main version: %lu, minor version: %lu, patch version: %lu\n", + server_version, major_version, minor_version, patch_version); } if (strstr(server_version, "MariaDB") != NULL) { @@ -292,11 +307,15 @@ int main(int argc, char **argv) { } /* Save replica status in replica_result */ - snprintf(replica_result, REPLICA_RESULTSIZE, "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s", row[replica_io_field], - row[replica_sql_field], seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown"); - - /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no mysqldump threads running */ - if (strcmp(row[replica_io_field], "Yes") != 0 || strcmp(row[replica_sql_field], "Yes") != 0) { + snprintf(replica_result, REPLICA_RESULTSIZE, + "Replica IO: %s Replica SQL: %s Seconds Behind Master: %s", + row[replica_io_field], row[replica_sql_field], + seconds_behind_field != -1 ? row[seconds_behind_field] : "Unknown"); + + /* Raise critical error if SQL THREAD or IO THREAD are stopped, but only if there are no + * mysqldump threads running */ + if (strcmp(row[replica_io_field], "Yes") != 0 || + strcmp(row[replica_sql_field], "Yes") != 0) { MYSQL_RES *res_mysqldump; MYSQL_ROW row_mysqldump; unsigned int mysqldump_threads = 0; @@ -325,20 +344,23 @@ int main(int argc, char **argv) { if (seconds_behind_field == -1) { printf("seconds_behind_field not found\n"); } else { - printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field, row[seconds_behind_field]); + printf("seconds_behind_field(index %d)=%s\n", seconds_behind_field, + row[seconds_behind_field]); } } /* Check Seconds Behind against threshold */ - if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && strcmp(row[seconds_behind_field], "NULL") != 0)) { + if ((seconds_behind_field != -1) && (row[seconds_behind_field] != NULL && + strcmp(row[seconds_behind_field], "NULL") != 0)) { double value = atof(row[seconds_behind_field]); int status; status = get_status(value, config.my_threshold); xasprintf(&perf, "%s %s", perf, - fperfdata("seconds behind master", value, "s", true, (double)config.warning_time, true, - (double)config.critical_time, false, 0, false, 0)); + fperfdata("seconds behind master", value, "s", true, + (double)config.warning_time, true, (double)config.critical_time, + false, 0, false, 0)); if (status == STATE_WARNING) { printf("SLOW_REPLICA %s: %s|%s\n", _("WARNING"), replica_result, perf); @@ -410,7 +432,8 @@ check_mysql_config_wrapper process_arguments(int argc, char **argv) { int option = 0; while (true) { - int option_index = getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); + int option_index = + getopt_long(argc, argv, "hlvVnSP:p:u:d:H:s:c:w:a:k:C:D:L:f:g:", longopts, &option); if (option_index == -1 || option_index == EOF) { break; @@ -580,15 +603,17 @@ void print_help(void) { printf(" ==> %s <==\n", _("IMPORTANT: THIS FORM OF AUTHENTICATION IS NOT SECURE!!!")); printf(" %s\n", _("Your clear-text password could be visible as a process table entry")); printf(" %s\n", "-S, --check-slave"); - printf(" %s\n", - _("Check if the slave thread is running properly. This option is deprecated in favour of check-replica, which does the same")); + printf(" %s\n", _("Check if the slave thread is running properly. This option is deprecated " + "in favour of check-replica, which does the same")); printf(" %s\n", "--check-replica"); printf(" %s\n", _("Check if the replica thread is running properly.")); printf(" %s\n", "-w, --warning"); - printf(" %s\n", _("Exit with WARNING status if replica server is more than INTEGER seconds")); + printf(" %s\n", + _("Exit with WARNING status if replica server is more than INTEGER seconds")); printf(" %s\n", _("behind master")); printf(" %s\n", "-c, --critical"); - printf(" %s\n", _("Exit with CRITICAL status if replica server is more then INTEGER seconds")); + printf(" %s\n", + _("Exit with CRITICAL status if replica server is more then INTEGER seconds")); printf(" %s\n", _("behind master")); printf(" %s\n", "-l, --ssl"); printf(" %s\n", _("Use ssl encryption")); @@ -604,7 +629,8 @@ void print_help(void) { printf(" %s\n", _("List of valid SSL ciphers")); printf("\n"); - printf(" %s\n", _("There are no required arguments. By default, the local database is checked")); + printf(" %s\n", + _("There are no required arguments. By default, the local database is checked")); printf(" %s\n", _("using the default unix socket. You can force TCP on localhost by using an")); printf(" %s\n", _("IP address or FQDN ('localhost' will use the socket as well).")); diff --git a/plugins/check_mysql_query.c b/plugins/check_mysql_query.c index 5e04a94b..c7e84deb 100644 --- a/plugins/check_mysql_query.c +++ b/plugins/check_mysql_query.c @@ -47,7 +47,8 @@ typedef struct { check_mysql_query_config config; } check_mysql_query_config_wrapper; static check_mysql_query_config_wrapper process_arguments(int /*argc*/, char ** /*argv*/); -static check_mysql_query_config_wrapper validate_arguments(check_mysql_query_config_wrapper /*config_wrapper*/); +static check_mysql_query_config_wrapper + validate_arguments(check_mysql_query_config_wrapper /*config_wrapper*/); static void print_help(void); void print_usage(void); @@ -83,7 +84,8 @@ int main(int argc, char **argv) { } /* establish a connection to the server and error checking */ - if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, config.db_port, config.db_socket, 0)) { + if (!mysql_real_connect(&mysql, config.db_host, config.db_user, config.db_pass, config.db, + config.db_port, config.db_socket, 0)) { if (mysql_errno(&mysql) == CR_UNKNOWN_HOST) { die(STATE_WARNING, "QUERY %s: %s\n", _("WARNING"), mysql_error(&mysql)); } else if (mysql_errno(&mysql) == CR_VERSION_ERROR) { @@ -155,8 +157,11 @@ int main(int argc, char **argv) { printf("QUERY %s: ", _("CRITICAL")); } printf(_("'%s' returned %f | %s"), config.sql_query, value, - fperfdata("result", value, "", config.my_thresholds->warning, config.my_thresholds->warning ? config.my_thresholds->warning->end : 0, - config.my_thresholds->critical, config.my_thresholds->critical ? config.my_thresholds->critical->end : 0, false, 0, false, 0)); + fperfdata("result", value, "", config.my_thresholds->warning, + config.my_thresholds->warning ? config.my_thresholds->warning->end : 0, + config.my_thresholds->critical, + config.my_thresholds->critical ? config.my_thresholds->critical->end : 0, + false, 0, false, 0)); printf("\n"); return status; @@ -164,12 +169,21 @@ int main(int argc, char **argv) { /* process command-line arguments */ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { - static struct option longopts[] = { - {"hostname", required_argument, 0, 'H'}, {"socket", required_argument, 0, 's'}, {"database", required_argument, 0, 'd'}, - {"username", required_argument, 0, 'u'}, {"password", required_argument, 0, 'p'}, {"file", required_argument, 0, 'f'}, - {"group", required_argument, 0, 'g'}, {"port", required_argument, 0, 'P'}, {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"query", required_argument, 0, 'q'}, - {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {0, 0, 0, 0}}; + static struct option longopts[] = {{"hostname", required_argument, 0, 'H'}, + {"socket", required_argument, 0, 's'}, + {"database", required_argument, 0, 'd'}, + {"username", required_argument, 0, 'u'}, + {"password", required_argument, 0, 'p'}, + {"file", required_argument, 0, 'f'}, + {"group", required_argument, 0, 'g'}, + {"port", required_argument, 0, 'P'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"help", no_argument, 0, 'h'}, + {"query", required_argument, 0, 'q'}, + {"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, + {0, 0, 0, 0}}; check_mysql_query_config_wrapper result = { .errorcode = OK, @@ -255,7 +269,8 @@ check_mysql_query_config_wrapper process_arguments(int argc, char **argv) { return validate_arguments(result); } -check_mysql_query_config_wrapper validate_arguments(check_mysql_query_config_wrapper config_wrapper) { +check_mysql_query_config_wrapper +validate_arguments(check_mysql_query_config_wrapper config_wrapper) { if (config_wrapper.config.sql_query == NULL) { usage("Must specify a SQL query to run"); } diff --git a/plugins/check_nt.c b/plugins/check_nt.c index 7dd23e5c..35ca92cd 100644 --- a/plugins/check_nt.c +++ b/plugins/check_nt.c @@ -96,7 +96,8 @@ int main(int argc, char **argv) { xasprintf(&send_buffer, "%s&1", config.req_password); fetch_data(config.server_address, config.server_port, send_buffer); if (config.value_list != NULL && strcmp(recv_buffer, config.value_list) != 0) { - xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"), recv_buffer, config.value_list); + xasprintf(&output_message, _("Wrong client version - running: %s, required: %s"), + recv_buffer, config.value_list); return_code = STATE_WARNING; } else { xasprintf(&output_message, "%s", recv_buffer); @@ -116,9 +117,12 @@ int main(int argc, char **argv) { /* loop until one of the parameters is wrong or not present */ int offset = 0; - while (lvalue_list[0 + offset] > (unsigned long)0 && lvalue_list[0 + offset] <= (unsigned long)17280 && - lvalue_list[1 + offset] > (unsigned long)0 && lvalue_list[1 + offset] <= (unsigned long)100 && - lvalue_list[2 + offset] > (unsigned long)0 && lvalue_list[2 + offset] <= (unsigned long)100) { + while (lvalue_list[0 + offset] > (unsigned long)0 && + lvalue_list[0 + offset] <= (unsigned long)17280 && + lvalue_list[1 + offset] > (unsigned long)0 && + lvalue_list[1 + offset] <= (unsigned long)100 && + lvalue_list[2 + offset] > (unsigned long)0 && + lvalue_list[2 + offset] <= (unsigned long)100) { /* Send request and retrieve data */ xasprintf(&send_buffer, "%s&2&%lu", config.req_password, lvalue_list[0 + offset]); @@ -133,10 +137,12 @@ int main(int argc, char **argv) { return_code = STATE_WARNING; } - xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization, lvalue_list[0 + offset]); + xasprintf(&output_message, _(" %lu%% (%lu min average)"), utilization, + lvalue_list[0 + offset]); xasprintf(&temp_string, "%s%s", temp_string, output_message); - xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"), lvalue_list[0 + offset], utilization, - lvalue_list[1 + offset], lvalue_list[2 + offset]); + xasprintf(&perfdata, _(" '%lu min avg Load'=%lu%%;%lu;%lu;0;100"), + lvalue_list[0 + offset], utilization, lvalue_list[1 + offset], + lvalue_list[2 + offset]); xasprintf(&temp_string_perf, "%s%s", temp_string_perf, perfdata); offset += 3; /* move across the array */ } @@ -154,8 +160,10 @@ int main(int argc, char **argv) { if (config.value_list == NULL) { tmp_value_list = "minutes"; } - if (strncmp(tmp_value_list, "seconds", strlen("seconds") + 1) && strncmp(tmp_value_list, "minutes", strlen("minutes") + 1) && - strncmp(config.value_list, "hours", strlen("hours") + 1) && strncmp(tmp_value_list, "days", strlen("days") + 1)) { + if (strncmp(tmp_value_list, "seconds", strlen("seconds") + 1) && + strncmp(tmp_value_list, "minutes", strlen("minutes") + 1) && + strncmp(config.value_list, "hours", strlen("hours") + 1) && + strncmp(tmp_value_list, "days", strlen("days") + 1)) { output_message = strdup(_("wrong -l argument")); } else { @@ -175,8 +183,9 @@ int main(int argc, char **argv) { } /* else uptime in seconds, nothing to do */ - xasprintf(&output_message, _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays, uphours, upminutes, - uptime); + xasprintf(&output_message, + _("System Uptime - %u day(s) %u hour(s) %u minute(s) |uptime=%lu"), updays, + uphours, upminutes, uptime); if (config.check_critical_value && uptime <= config.critical_value) { return_code = STATE_CRITICAL; @@ -207,20 +216,27 @@ int main(int argc, char **argv) { } if (total_disk_space > 0 && free_disk_space >= 0) { - double percent_used_space = ((total_disk_space - free_disk_space) / total_disk_space) * 100; + double percent_used_space = + ((total_disk_space - free_disk_space) / total_disk_space) * 100; double warning_used_space = ((float)config.warning_value / 100) * total_disk_space; - double critical_used_space = ((float)config.critical_value / 100) * total_disk_space; - - xasprintf(&temp_string, _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"), config.value_list, - total_disk_space / 1073741824, (total_disk_space - free_disk_space) / 1073741824, percent_used_space, - free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100); - xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"), config.value_list, - (total_disk_space - free_disk_space) / 1073741824, warning_used_space / 1073741824, - critical_used_space / 1073741824, total_disk_space / 1073741824); + double critical_used_space = + ((float)config.critical_value / 100) * total_disk_space; + + xasprintf( + &temp_string, + _("%s:\\ - total: %.2f Gb - used: %.2f Gb (%.0f%%) - free %.2f Gb (%.0f%%)"), + config.value_list, total_disk_space / 1073741824, + (total_disk_space - free_disk_space) / 1073741824, percent_used_space, + free_disk_space / 1073741824, (free_disk_space / total_disk_space) * 100); + xasprintf(&temp_string_perf, _("'%s:\\ Used Space'=%.2fGb;%.2f;%.2f;0.00;%.2f"), + config.value_list, (total_disk_space - free_disk_space) / 1073741824, + warning_used_space / 1073741824, critical_used_space / 1073741824, + total_disk_space / 1073741824); if (config.check_critical_value && percent_used_space >= config.critical_value) { return_code = STATE_CRITICAL; - } else if (config.check_warning_value && percent_used_space >= config.warning_value) { + } else if (config.check_warning_value && + percent_used_space >= config.warning_value) { return_code = STATE_WARNING; } else { return_code = STATE_OK; @@ -239,8 +255,10 @@ int main(int argc, char **argv) { if (config.value_list == NULL) { output_message = strdup(_("No service/process specified")); } else { - preparelist(config.value_list); /* replace , between services with & to send the request */ - xasprintf(&send_buffer, "%s&%u&%s&%s", config.req_password, (config.vars_to_check == CHECK_SERVICESTATE) ? 5 : 6, + preparelist( + config.value_list); /* replace , between services with & to send the request */ + xasprintf(&send_buffer, "%s&%u&%s&%s", config.req_password, + (config.vars_to_check == CHECK_SERVICESTATE) ? 5 : 6, (config.show_all) ? "ShowAll" : "ShowFail", config.value_list); fetch_data(config.server_address, config.server_port, send_buffer); char *numstr = strtok(recv_buffer, "&"); @@ -271,10 +289,14 @@ int main(int argc, char **argv) { /* Divisor should be 1048567, not 3044515, as we are measuring "Commit Charge" here, which equals RAM + Pagefiles. */ - xasprintf(&output_message, _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"), - mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space, (mem_commitLimit - mem_commitByte) / 1048567, - (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100); - xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"), mem_commitByte / 1048567, warning_used_space / 1048567, + xasprintf( + &output_message, + _("Memory usage: total:%.2f MB - used: %.2f MB (%.0f%%) - free: %.2f MB (%.0f%%)"), + mem_commitLimit / 1048567, mem_commitByte / 1048567, percent_used_space, + (mem_commitLimit - mem_commitByte) / 1048567, + (mem_commitLimit - mem_commitByte) / mem_commitLimit * 100); + xasprintf(&perfdata, _("'Memory usage'=%.2fMB;%.2f;%.2f;0.00;%.2f"), + mem_commitByte / 1048567, warning_used_space / 1048567, critical_used_space / 1048567, mem_commitLimit / 1048567); return_code = STATE_OK; @@ -302,16 +324,17 @@ int main(int argc, char **argv) { the counter unit - that is, the dimensions of the counter you're getting. Examples: pages/s, packets transferred, etc. - 4) If you want, you may provide the minimum and maximum values to expect. They aren't mandatory, - but once specified they MUST have the same order of magnitude and units of -w and -c; otherwise. - strange things will happen when you make graphs of your data. + 4) If you want, you may provide the minimum and maximum values to expect. They aren't + mandatory, but once specified they MUST have the same order of magnitude and units of -w and + -c; otherwise. strange things will happen when you make graphs of your data. */ double counter_value = 0.0; if (config.value_list == NULL) { output_message = strdup(_("No counter specified")); } else { - preparelist(config.value_list); /* replace , between services with & to send the request */ + preparelist( + config.value_list); /* replace , between services with & to send the request */ bool isPercent = (strchr(config.value_list, '%') != NULL); strtok(config.value_list, "&"); /* burn the first parameters */ @@ -358,15 +381,18 @@ int main(int argc, char **argv) { if (allRight) { /* Let's format the output string, finally... */ if (strstr(description, "%") == NULL) { - xasprintf(&output_message, "%s = %.2f %s", description, counter_value, counter_unit); + xasprintf(&output_message, "%s = %.2f %s", description, counter_value, + counter_unit); } else { /* has formatting, will segv if wrong */ xasprintf(&output_message, description, counter_value); } xasprintf(&output_message, "%s |", output_message); xasprintf(&output_message, "%s %s", output_message, - fperfdata(description, counter_value, counter_unit, 1, config.warning_value, 1, config.critical_value, - (!(isPercent) && (minval != NULL)), fminval, (!(isPercent) && (minval != NULL)), fmaxval)); + fperfdata(description, counter_value, counter_unit, 1, + config.warning_value, 1, config.critical_value, + (!(isPercent) && (minval != NULL)), fminval, + (!(isPercent) && (minval != NULL)), fmaxval)); } } @@ -391,7 +417,8 @@ int main(int argc, char **argv) { if (config.value_list == NULL) { output_message = strdup(_("No counter specified")); } else { - preparelist(config.value_list); /* replace , between services with & to send the request */ + preparelist( + config.value_list); /* replace , between services with & to send the request */ xasprintf(&send_buffer, "%s&9&%s", config.req_password, config.value_list); fetch_data(config.server_address, config.server_port, send_buffer); unsigned long age_in_minutes = atoi(strtok(recv_buffer, "&")); @@ -724,25 +751,31 @@ void print_help(void) { printf(" %s\n", "\"%%.f %%%% paging file used.\""); printf(" %s\n", "INSTANCES ="); printf(" %s\n", _("Check any performance counter object of Windows NT/2000.")); - printf(" %s\n", _("Syntax: check_nt -H -p -v INSTANCES -l ")); + printf(" %s\n", + _("Syntax: check_nt -H -p -v INSTANCES -l ")); printf(" %s\n", _(" is a Windows Perfmon Counter object (eg. Process),")); printf(" %s\n", _("if it is two words, it should be enclosed in quotes")); printf(" %s\n", _("The returned results will be a comma-separated list of instances on ")); printf(" %s\n", _(" the selected computer for that object.")); - printf(" %s\n", _("The purpose of this is to be run from command line to determine what instances")); - printf(" %s\n", _(" are available for monitoring without having to log onto the Windows server")); + printf(" %s\n", + _("The purpose of this is to be run from command line to determine what instances")); + printf(" %s\n", + _(" are available for monitoring without having to log onto the Windows server")); printf(" %s\n", _(" to run Perfmon directly.")); - printf(" %s\n", _("It can also be used in scripts that automatically create the monitoring service")); + printf(" %s\n", + _("It can also be used in scripts that automatically create the monitoring service")); printf(" %s\n", _(" configuration files.")); printf(" %s\n", _("Some examples:")); printf(" %s\n\n", _("check_nt -H 192.168.1.1 -p 1248 -v INSTANCES -l Process")); printf("%s\n", _("Notes:")); - printf(" %s\n", _("- The NSClient service should be running on the server to get any information")); + printf(" %s\n", + _("- The NSClient service should be running on the server to get any information")); printf(" %s\n", "(http://nsclient.ready2run.nl)."); printf(" %s\n", _("- Critical thresholds should be lower than warning thresholds")); printf(" %s\n", _("- Default port 1248 is sometimes in use by other services. The error")); - printf(" %s\n", _("output when this happens contains \"Cannot map xxxxx to protocol number\".")); + printf(" %s\n", + _("output when this happens contains \"Cannot map xxxxx to protocol number\".")); printf(" %s\n", _("One fix for this is to change the port to something else on check_nt ")); printf(" %s\n", _("and on the client service it\'s connecting to.")); diff --git a/plugins/check_ntp.c b/plugins/check_ntp.c index d33f8786..b22cc3c1 100644 --- a/plugins/check_ntp.c +++ b/plugins/check_ntp.c @@ -1,34 +1,34 @@ /***************************************************************************** -* -* Monitoring check_ntp plugin -* -* License: GPL -* Copyright (c) 2006 Sean Finney -* Copyright (c) 2006-2024 Monitoring Plugins Development Team -* -* Description: -* -* This file contains the check_ntp plugin -* -* This plugin to check ntp servers independent of any commandline -* programs or external libraries. -* -* -* 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_ntp plugin + * + * License: GPL + * Copyright (c) 2006 Sean Finney + * Copyright (c) 2006-2024 Monitoring Plugins Development Team + * + * Description: + * + * This file contains the check_ntp plugin + * + * This plugin to check ntp servers independent of any commandline + * programs or external libraries. + * + * + * 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 . + * + * + *****************************************************************************/ const char *progname = "check_ntp"; const char *copyright = "2006-2024"; @@ -38,24 +38,24 @@ const char *email = "devel@monitoring-plugins.org"; #include "netutils.h" #include "utils.h" -static char *server_address=NULL; -static int verbose=0; +static char *server_address = NULL; +static int verbose = 0; static bool do_offset = false; -static char *owarn="60"; -static char *ocrit="120"; +static char *owarn = "60"; +static char *ocrit = "120"; static bool do_jitter = false; -static char *jwarn="5000"; -static char *jcrit="10000"; +static char *jwarn = "5000"; +static char *jcrit = "10000"; -static int process_arguments (int /*argc*/, char ** /*argv*/); +static int process_arguments(int /*argc*/, char ** /*argv*/); static thresholds *offset_thresholds = NULL; static thresholds *jitter_thresholds = NULL; -static void print_help (void); -void print_usage (void); +static void print_help(void); +void print_usage(void); /* number of times to perform each request to get a good average. */ #ifndef AVG_NUM -#define AVG_NUM 4 +# define AVG_NUM 4 #endif /* max size of control message data */ @@ -63,17 +63,17 @@ void print_usage (void); /* this structure holds everything in an ntp request/response as per rfc1305 */ typedef struct { - uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ - uint8_t stratum; /* clock stratum */ - int8_t poll; /* polling interval */ - int8_t precision; /* precision of the local clock */ - int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ - uint32_t rtdisp; /* like above, but for max err to primary src */ - uint32_t refid; /* ref clock identifier */ - uint64_t refts; /* reference timestamp. local time local clock */ - uint64_t origts; /* time at which request departed client */ - uint64_t rxts; /* time at which request arrived at server */ - uint64_t txts; /* time at which request departed server */ + uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ + uint8_t stratum; /* clock stratum */ + int8_t poll; /* polling interval */ + int8_t precision; /* precision of the local clock */ + int32_t rtdelay; /* total rt delay, as a fixed point num. see macros */ + uint32_t rtdisp; /* like above, but for max err to primary src */ + uint32_t refid; /* ref clock identifier */ + uint64_t refts; /* reference timestamp. local time local clock */ + uint64_t origts; /* time at which request departed client */ + uint64_t rxts; /* time at which request arrived at server */ + uint64_t txts; /* time at which request departed server */ } ntp_message; /* this structure holds data about results from querying offset from a peer */ @@ -84,20 +84,20 @@ typedef struct { double rtdelay; /* converted from the ntp_message */ double rtdisp; /* converted from the ntp_message */ double offset[AVG_NUM]; /* offsets from each response */ - uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ + uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ } ntp_server_results; /* this structure holds everything in an ntp control message as per rfc1305 */ typedef struct { - uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ - uint8_t op; /* R,E,M bits and Opcode */ - uint16_t seq; /* Packet sequence */ - uint16_t status; /* Clock status */ - uint16_t assoc; /* Association */ - uint16_t offset; /* Similar to TCP sequence # */ - uint16_t count; /* # bytes of data */ + uint8_t flags; /* byte with leapindicator,vers,mode. see macros */ + uint8_t op; /* R,E,M bits and Opcode */ + uint16_t seq; /* Packet sequence */ + uint16_t status; /* Clock status */ + uint16_t assoc; /* Association */ + uint16_t offset; /* Similar to TCP sequence # */ + uint16_t count; /* # bytes of data */ char data[MAX_CM_SIZE]; /* ASCII data of the request */ - /* NB: not necessarily NULL terminated! */ + /* NB: not necessarily NULL terminated! */ } ntp_control_message; /* this is an association/status-word pair found in control packet responses */ @@ -108,38 +108,50 @@ typedef struct { /* bits 1,2 are the leap indicator */ #define LI_MASK 0xc0 -#define LI(x) ((x&LI_MASK)>>6) -#define LI_SET(x,y) do{ x |= ((y<<6)&LI_MASK); }while(0) +#define LI(x) ((x & LI_MASK) >> 6) +#define LI_SET(x, y) \ + do { \ + x |= ((y << 6) & LI_MASK); \ + } while (0) /* and these are the values of the leap indicator */ -#define LI_NOWARNING 0x00 -#define LI_EXTRASEC 0x01 +#define LI_NOWARNING 0x00 +#define LI_EXTRASEC 0x01 #define LI_MISSINGSEC 0x02 -#define LI_ALARM 0x03 +#define LI_ALARM 0x03 /* bits 3,4,5 are the ntp version */ #define VN_MASK 0x38 -#define VN(x) ((x&VN_MASK)>>3) -#define VN_SET(x,y) do{ x |= ((y<<3)&VN_MASK); }while(0) +#define VN(x) ((x & VN_MASK) >> 3) +#define VN_SET(x, y) \ + do { \ + x |= ((y << 3) & VN_MASK); \ + } while (0) #define VN_RESERVED 0x02 /* bits 6,7,8 are the ntp mode */ #define MODE_MASK 0x07 -#define MODE(x) (x&MODE_MASK) -#define MODE_SET(x,y) do{ x |= (y&MODE_MASK); }while(0) +#define MODE(x) (x & MODE_MASK) +#define MODE_SET(x, y) \ + do { \ + x |= (y & MODE_MASK); \ + } while (0) /* here are some values */ -#define MODE_CLIENT 0x03 +#define MODE_CLIENT 0x03 #define MODE_CONTROLMSG 0x06 /* In control message, bits 8-10 are R,E,M bits */ -#define REM_MASK 0xe0 -#define REM_RESP 0x80 +#define REM_MASK 0xe0 +#define REM_RESP 0x80 #define REM_ERROR 0x40 -#define REM_MORE 0x20 +#define REM_MORE 0x20 /* In control message, bits 11 - 15 are opcode */ #define OP_MASK 0x1f -#define OP_SET(x,y) do{ x |= (y&OP_MASK); }while(0) +#define OP_SET(x, y) \ + do { \ + x |= (y & OP_MASK); \ + } while (0) #define OP_READSTAT 0x01 #define OP_READVAR 0x02 /* In peer status bytes, bits 6,7,8 determine clock selection status */ -#define PEER_SEL(x) ((ntohs(x)>>8)&0x07) -#define PEER_INCLUDED 0x04 +#define PEER_SEL(x) ((ntohs(x) >> 8) & 0x07) +#define PEER_INCLUDED 0x04 #define PEER_SYNCSOURCE 0x06 /** @@ -153,82 +165,92 @@ typedef struct { /* macros to access the left/right 16 bits of a 32-bit ntp "fixed point" number. note that these can be used as lvalues too */ -#define L16(x) (((uint16_t*)&x)[0]) -#define R16(x) (((uint16_t*)&x)[1]) +#define L16(x) (((uint16_t *)&x)[0]) +#define R16(x) (((uint16_t *)&x)[1]) /* macros to access the left/right 32 bits of a 64-bit ntp "fixed point" number. these too can be used as lvalues */ -#define L32(x) (((uint32_t*)&x)[0]) -#define R32(x) (((uint32_t*)&x)[1]) +#define L32(x) (((uint32_t *)&x)[0]) +#define R32(x) (((uint32_t *)&x)[1]) /* ntp wants seconds since 1/1/00, epoch is 1/1/70. this is the difference */ #define EPOCHDIFF 0x83aa7e80UL /* extract a 32-bit ntp fixed point number into a double */ -#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x))/65536.0) +#define NTP32asDOUBLE(x) (ntohs(L16(x)) + (double)ntohs(R16(x)) / 65536.0) /* likewise for a 64-bit ntp fp number */ -#define NTP64asDOUBLE(n) (double)(((uint64_t)n)?\ - (ntohl(L32(n))-EPOCHDIFF) + \ - (.00000001*(0.5+(double)(ntohl(R32(n))/42.94967296))):\ - 0) +#define NTP64asDOUBLE(n) \ + (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \ + (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \ + : 0) /* convert a struct timeval to a double */ -#define TVasDOUBLE(x) (double)(x.tv_sec+(0.000001*x.tv_usec)) +#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) /* convert an ntp 64-bit fp number to a struct timeval */ -#define NTP64toTV(n,t) \ - do{ if(!n) t.tv_sec = t.tv_usec = 0; \ - else { \ - t.tv_sec=ntohl(L32(n))-EPOCHDIFF; \ - t.tv_usec=(int)(0.5+(double)(ntohl(R32(n))/4294.967296)); \ - } \ - }while(0) +#define NTP64toTV(n, t) \ + do { \ + if (!n) \ + t.tv_sec = t.tv_usec = 0; \ + else { \ + t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ + t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ + } \ + } while (0) /* convert a struct timeval to an ntp 64-bit fp number */ -#define TVtoNTP64(t,n) \ - do{ if(!t.tv_usec && !t.tv_sec) n=0x0UL; \ - else { \ - L32(n)=htonl(t.tv_sec + EPOCHDIFF); \ - R32(n)=htonl((uint64_t)((4294.967296*t.tv_usec)+.5)); \ - } \ - } while(0) +#define TVtoNTP64(t, n) \ + do { \ + if (!t.tv_usec && !t.tv_sec) \ + n = 0x0UL; \ + else { \ + L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ + R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ + } \ + } while (0) /* NTP control message header is 12 bytes, plus any data in the data * field, plus null padding to the nearest 32-bit boundary per rfc. */ -#define SIZEOF_NTPCM(m) (12+ntohs(m.count)+((ntohs(m.count)%4)?4-(ntohs(m.count)%4):0)) +#define SIZEOF_NTPCM(m) \ + (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) /* finally, a little helper or two for debugging: */ -#define DBG(x) do{if(verbose>1){ x; }}while(0); -#define PRINTSOCKADDR(x) \ - do{ \ - printf("%u.%u.%u.%u", (x>>24)&0xff, (x>>16)&0xff, (x>>8)&0xff, x&0xff);\ - }while(0); +#define DBG(x) \ + do { \ + if (verbose > 1) { \ + x; \ + } \ + } while (0); +#define PRINTSOCKADDR(x) \ + do { \ + printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ + } while (0); /* calculate the offset of the local clock */ -static inline double calc_offset(const ntp_message *m, const struct timeval *t){ +static inline double calc_offset(const ntp_message *m, const struct timeval *t) { double client_tx, peer_rx, peer_tx, client_rx; client_tx = NTP64asDOUBLE(m->origts); peer_rx = NTP64asDOUBLE(m->rxts); peer_tx = NTP64asDOUBLE(m->txts); - client_rx=TVasDOUBLE((*t)); - return (.5*((peer_tx-client_rx)+(peer_rx-client_tx))); + client_rx = TVasDOUBLE((*t)); + return (.5 * ((peer_tx - client_rx) + (peer_rx - client_tx))); } /* print out a ntp packet in human readable/debuggable format */ -void print_ntp_message(const ntp_message *p){ +void print_ntp_message(const ntp_message *p) { struct timeval ref, orig, rx, tx; - NTP64toTV(p->refts,ref); - NTP64toTV(p->origts,orig); - NTP64toTV(p->rxts,rx); - NTP64toTV(p->txts,tx); + NTP64toTV(p->refts, ref); + NTP64toTV(p->origts, orig); + NTP64toTV(p->rxts, rx); + NTP64toTV(p->txts, tx); printf("packet contents:\n"); printf("\tflags: 0x%.2x\n", p->flags); - printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); - printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); - printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); + printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); + printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); + printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); printf("\tstratum = %d\n", p->stratum); printf("\tpoll = %g\n", pow(2, p->poll)); printf("\tprecision = %g\n", pow(2, p->precision)); @@ -241,32 +263,31 @@ void print_ntp_message(const ntp_message *p){ printf("\ttxts = %-.16g\n", NTP64asDOUBLE(p->txts)); } -void print_ntp_control_message(const ntp_control_message *p){ - int i=0, numpeers=0; - const ntp_assoc_status_pair *peer=NULL; +void print_ntp_control_message(const ntp_control_message *p) { + int i = 0, numpeers = 0; + const ntp_assoc_status_pair *peer = NULL; printf("control packet contents:\n"); printf("\tflags: 0x%.2x , 0x%.2x\n", p->flags, p->op); - printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags&LI_MASK); - printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags&VN_MASK); - printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags&MODE_MASK); - printf("\t response=%d (0x%.2x)\n", (p->op&REM_RESP)>0, p->op&REM_RESP); - printf("\t more=%d (0x%.2x)\n", (p->op&REM_MORE)>0, p->op&REM_MORE); - printf("\t error=%d (0x%.2x)\n", (p->op&REM_ERROR)>0, p->op&REM_ERROR); - printf("\t op=%d (0x%.2x)\n", p->op&OP_MASK, p->op&OP_MASK); + printf("\t li=%d (0x%.2x)\n", LI(p->flags), p->flags & LI_MASK); + printf("\t vn=%d (0x%.2x)\n", VN(p->flags), p->flags & VN_MASK); + printf("\t mode=%d (0x%.2x)\n", MODE(p->flags), p->flags & MODE_MASK); + printf("\t response=%d (0x%.2x)\n", (p->op & REM_RESP) > 0, p->op & REM_RESP); + printf("\t more=%d (0x%.2x)\n", (p->op & REM_MORE) > 0, p->op & REM_MORE); + printf("\t error=%d (0x%.2x)\n", (p->op & REM_ERROR) > 0, p->op & REM_ERROR); + printf("\t op=%d (0x%.2x)\n", p->op & OP_MASK, p->op & OP_MASK); printf("\tsequence: %d (0x%.2x)\n", ntohs(p->seq), ntohs(p->seq)); printf("\tstatus: %d (0x%.2x)\n", ntohs(p->status), ntohs(p->status)); printf("\tassoc: %d (0x%.2x)\n", ntohs(p->assoc), ntohs(p->assoc)); printf("\toffset: %d (0x%.2x)\n", ntohs(p->offset), ntohs(p->offset)); printf("\tcount: %d (0x%.2x)\n", ntohs(p->count), ntohs(p->count)); - numpeers=ntohs(p->count)/(sizeof(ntp_assoc_status_pair)); - if(p->op&REM_RESP && p->op&OP_READSTAT){ - peer=(ntp_assoc_status_pair*)p->data; - for(i=0;i= PEER_INCLUDED){ - if(PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE){ + numpeers = ntohs(p->count) / (sizeof(ntp_assoc_status_pair)); + if (p->op & REM_RESP && p->op & OP_READSTAT) { + peer = (ntp_assoc_status_pair *)p->data; + for (i = 0; i < numpeers; i++) { + printf("\tpeer id %.2x status %.2x", ntohs(peer[i].assoc), ntohs(peer[i].status)); + if (PEER_SEL(peer[i].status) >= PEER_INCLUDED) { + if (PEER_SEL(peer[i].status) >= PEER_SYNCSOURCE) { printf(" <-- current sync source"); } else { printf(" <-- current sync candidate"); @@ -277,41 +298,45 @@ void print_ntp_control_message(const ntp_control_message *p){ } } -void setup_request(ntp_message *p){ +void setup_request(ntp_message *p) { struct timeval t; memset(p, 0, sizeof(ntp_message)); LI_SET(p->flags, LI_ALARM); VN_SET(p->flags, 4); MODE_SET(p->flags, MODE_CLIENT); - p->poll=4; - p->precision=(int8_t)0xfa; - L16(p->rtdelay)=htons(1); - L16(p->rtdisp)=htons(1); + p->poll = 4; + p->precision = (int8_t)0xfa; + L16(p->rtdelay) = htons(1); + L16(p->rtdisp) = htons(1); gettimeofday(&t, NULL); - TVtoNTP64(t,p->txts); + TVtoNTP64(t, p->txts); } /* select the "best" server from a list of servers, and return its index. * this is done by filtering servers based on stratum, dispersion, and * finally round-trip delay. */ -int best_offset_server(const ntp_server_results *slist, int nservers){ - int cserver=0, best_server=-1; +int best_offset_server(const ntp_server_results *slist, int nservers) { + int cserver = 0, best_server = -1; /* for each server */ - for(cserver=0; cserver= 0) { + if (best_server >= 0) { DBG(printf("best server selected: peer %d\n", best_server)); return best_server; } else { @@ -354,16 +379,16 @@ int best_offset_server(const ntp_server_results *slist, int nservers){ * we don't waste time sitting around waiting for single packets. * - we also "manually" handle resolving host names and connecting, because * we have to do it in a way that our lazy macros don't handle currently :( */ -double offset_request(const char *host, int *status){ - int i=0, ga_result=0, num_hosts=0, *socklist=NULL, respnum=0; - int servers_completed=0, one_read=0, servers_readable=0, best_index=-1; - time_t now_time=0, start_ts=0; - ntp_message *req=NULL; - double avg_offset=0.; +double offset_request(const char *host, int *status) { + int i = 0, ga_result = 0, num_hosts = 0, *socklist = NULL, respnum = 0; + int servers_completed = 0, one_read = 0, servers_readable = 0, best_index = -1; + time_t now_time = 0, start_ts = 0; + ntp_message *req = NULL; + double avg_offset = 0.; struct timeval recv_time; - struct addrinfo *ai=NULL, *ai_tmp=NULL, hints; - struct pollfd *ufds=NULL; - ntp_server_results *servers=NULL; + struct addrinfo *ai = NULL, *ai_tmp = NULL, hints; + struct pollfd *ufds = NULL; + ntp_server_results *servers = NULL; /* setup hints to only return results from getaddrinfo that we'd like */ memset(&hints, 0, sizeof(struct addrinfo)); @@ -373,97 +398,112 @@ double offset_request(const char *host, int *status){ /* fill in ai with the list of hosts resolved by the host name */ ga_result = getaddrinfo(host, "123", &hints, &ai); - if(ga_result!=0){ - die(STATE_UNKNOWN, "error getting address for %s: %s\n", - host, gai_strerror(ga_result)); + if (ga_result != 0) { + die(STATE_UNKNOWN, "error getting address for %s: %s\n", host, gai_strerror(ga_result)); } /* count the number of returned hosts, and allocate stuff accordingly */ - for(ai_tmp=ai; ai_tmp!=NULL; ai_tmp=ai_tmp->ai_next){ num_hosts++; } - req=(ntp_message*)malloc(sizeof(ntp_message)*num_hosts); - if(req==NULL) die(STATE_UNKNOWN, "can not allocate ntp message array"); - socklist=(int*)malloc(sizeof(int)*num_hosts); - if(socklist==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); - ufds=(struct pollfd*)malloc(sizeof(struct pollfd)*num_hosts); - if(ufds==NULL) die(STATE_UNKNOWN, "can not allocate socket array"); - servers=(ntp_server_results*)malloc(sizeof(ntp_server_results)*num_hosts); - if(servers==NULL) die(STATE_UNKNOWN, "can not allocate server array"); - memset(servers, 0, sizeof(ntp_server_results)*num_hosts); + for (ai_tmp = ai; ai_tmp != NULL; ai_tmp = ai_tmp->ai_next) { + num_hosts++; + } + req = (ntp_message *)malloc(sizeof(ntp_message) * num_hosts); + if (req == NULL) { + die(STATE_UNKNOWN, "can not allocate ntp message array"); + } + socklist = (int *)malloc(sizeof(int) * num_hosts); + if (socklist == NULL) { + die(STATE_UNKNOWN, "can not allocate socket array"); + } + ufds = (struct pollfd *)malloc(sizeof(struct pollfd) * num_hosts); + if (ufds == NULL) { + die(STATE_UNKNOWN, "can not allocate socket array"); + } + servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); + if (servers == NULL) { + die(STATE_UNKNOWN, "can not allocate server array"); + } + memset(servers, 0, sizeof(ntp_server_results) * num_hosts); DBG(printf("Found %d peers to check\n", num_hosts)); /* setup each socket for writing, and the corresponding struct pollfd */ - ai_tmp=ai; - for(i=0;ai_tmp;i++){ - socklist[i]=socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); - if(socklist[i] == -1) { + ai_tmp = ai; + for (i = 0; ai_tmp; i++) { + socklist[i] = socket(ai_tmp->ai_family, SOCK_DGRAM, IPPROTO_UDP); + if (socklist[i] == -1) { perror(NULL); die(STATE_UNKNOWN, "can not create new socket"); } - if(connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)){ + if (connect(socklist[i], ai_tmp->ai_addr, ai_tmp->ai_addrlen)) { /* don't die here, because it is enough if there is one server answering in time. This also would break for dual ipv4/6 stacked ntp servers when the client only supports on of them. */ DBG(printf("can't create socket connection on peer %i: %s\n", i, strerror(errno))); } else { - ufds[i].fd=socklist[i]; - ufds[i].events=POLLIN; - ufds[i].revents=0; + ufds[i].fd = socklist[i]; + ufds[i].events = POLLIN; + ufds[i].revents = 0; } ai_tmp = ai_tmp->ai_next; } /* now do AVG_NUM checks to each host. we stop before timeout/2 seconds * have passed in order to ensure post-processing and jitter time. */ - now_time=start_ts=time(NULL); - while(servers_completedflags, LI_NOWARNING); VN_SET(p->flags, VN_RESERVED); @@ -512,16 +553,16 @@ setup_control_request(ntp_control_message *p, uint8_t opcode, uint16_t seq){ } /* XXX handle responses with the error bit set */ -double jitter_request(int *status){ - int conn=-1, i, npeers=0, num_candidates=0; +double jitter_request(int *status) { + int conn = -1, i, npeers = 0, num_candidates = 0; bool syncsource_found = false; - int run=0, min_peer_sel=PEER_INCLUDED, num_selected=0, num_valid=0; - int peers_size=0, peer_offset=0; - ntp_assoc_status_pair *peers=NULL; + int run = 0, min_peer_sel = PEER_INCLUDED, num_selected = 0, num_valid = 0; + int peers_size = 0, peer_offset = 0; + ntp_assoc_status_pair *peers = NULL; ntp_control_message req; const char *getvar = "jitter"; double rval = 0.0, jitter = -1.0; - char *startofvalue=NULL, *nptr=NULL; + char *startofvalue = NULL, *nptr = NULL; void *tmp; /* Long-winded explanation: @@ -542,54 +583,62 @@ double jitter_request(int *status){ /* keep sending requests until the server stops setting the * REM_MORE bit, though usually this is only 1 packet. */ - do{ + do { setup_control_request(&req, OP_READSTAT, 1); DBG(printf("sending READSTAT request")); write(conn, &req, SIZEOF_NTPCM(req)); DBG(print_ntp_control_message(&req)); /* Attempt to read the largest size packet possible */ - req.count=htons(MAX_CM_SIZE); + req.count = htons(MAX_CM_SIZE); DBG(printf("receiving READSTAT response")) read(conn, &req, SIZEOF_NTPCM(req)); DBG(print_ntp_control_message(&req)); /* Each peer identifier is 4 bytes in the data section, which - * we represent as a ntp_assoc_status_pair datatype. - */ - peers_size+=ntohs(req.count); - if((tmp=realloc(peers, peers_size)) == NULL) + * we represent as a ntp_assoc_status_pair datatype. + */ + peers_size += ntohs(req.count); + if ((tmp = realloc(peers, peers_size)) == NULL) { free(peers), die(STATE_UNKNOWN, "can not (re)allocate 'peers' buffer\n"); - peers=tmp; - memcpy((void*)((ptrdiff_t)peers+peer_offset), (void*)req.data, ntohs(req.count)); - npeers=peers_size/sizeof(ntp_assoc_status_pair); - peer_offset+=ntohs(req.count); - } while(req.op&REM_MORE); + } + peers = tmp; + memcpy((void *)((ptrdiff_t)peers + peer_offset), (void *)req.data, ntohs(req.count)); + npeers = peers_size / sizeof(ntp_assoc_status_pair); + peer_offset += ntohs(req.count); + } while (req.op & REM_MORE); /* first, let's find out if we have a sync source, or if there are * at least some candidates. in the case of the latter we'll issue * a warning but go ahead with the check on them. */ - for (i = 0; i < npeers; i++){ - if (PEER_SEL(peers[i].status) >= PEER_INCLUDED){ + for (i = 0; i < npeers; i++) { + if (PEER_SEL(peers[i].status) >= PEER_INCLUDED) { num_candidates++; - if(PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE){ + if (PEER_SEL(peers[i].status) >= PEER_SYNCSOURCE) { syncsource_found = true; - min_peer_sel=PEER_SYNCSOURCE; + min_peer_sel = PEER_SYNCSOURCE; } } } - if(verbose) printf("%d candidate peers available\n", num_candidates); - if(verbose && syncsource_found) printf("synchronization source found\n"); - if(! syncsource_found){ + if (verbose) { + printf("%d candidate peers available\n", num_candidates); + } + if (verbose && syncsource_found) { + printf("synchronization source found\n"); + } + if (!syncsource_found) { *status = STATE_UNKNOWN; - if(verbose) printf("warning: no synchronization source found\n"); + if (verbose) { + printf("warning: no synchronization source found\n"); + } } - - for (run=0; run= min_peer_sel){ - char jitter_data[MAX_CM_SIZE+1]; + if (PEER_SEL(peers[i].status) >= min_peer_sel) { + char jitter_data[MAX_CM_SIZE + 1]; size_t jitter_data_count; num_selected++; @@ -602,7 +651,7 @@ double jitter_request(int *status){ */ /* Older servers doesn't know what jitter is, so if we get an * error on the first pass we redo it with "dispersion" */ - strncpy(req.data, getvar, MAX_CM_SIZE-1); + strncpy(req.data, getvar, MAX_CM_SIZE - 1); req.count = htons(strlen(getvar)); DBG(printf("sending READVAR request...\n")); write(conn, &req, SIZEOF_NTPCM(req)); @@ -613,8 +662,11 @@ double jitter_request(int *status){ read(conn, &req, SIZEOF_NTPCM(req)); DBG(print_ntp_control_message(&req)); - if(req.op&REM_ERROR && strstr(getvar, "jitter")) { - if(verbose) printf("The 'jitter' command failed (old ntp server?)\nRestarting with 'dispersion'...\n"); + if (req.op & REM_ERROR && strstr(getvar, "jitter")) { + if (verbose) { + printf("The 'jitter' command failed (old ntp server?)\nRestarting with " + "'dispersion'...\n"); + } getvar = "dispersion"; num_selected--; i--; @@ -622,32 +674,33 @@ double jitter_request(int *status){ } /* get to the float value */ - if(verbose) { + if (verbose) { printf("parsing jitter from peer %.2x: ", ntohs(peers[i].assoc)); } - if((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)){ - die(STATE_UNKNOWN, - _("jitter response too large (%lu bytes)\n"), - (unsigned long)jitter_data_count); + if ((jitter_data_count = ntohs(req.count)) >= sizeof(jitter_data)) { + die(STATE_UNKNOWN, _("jitter response too large (%lu bytes)\n"), + (unsigned long)jitter_data_count); } memcpy(jitter_data, req.data, jitter_data_count); jitter_data[jitter_data_count] = '\0'; startofvalue = strchr(jitter_data, '='); - if(startofvalue != NULL) { + if (startofvalue != NULL) { startofvalue++; jitter = strtod(startofvalue, &nptr); } - if(startofvalue == NULL || startofvalue==nptr){ + if (startofvalue == NULL || startofvalue == nptr) { printf("warning: unable to read server jitter response.\n"); *status = STATE_UNKNOWN; } else { - if(verbose) printf("%g\n", jitter); + if (verbose) { + printf("%g\n", jitter); + } num_valid++; rval += jitter; } } } - if(verbose){ + if (verbose) { printf("jitter parsed from %d/%d peers\n", num_valid, num_selected); } } @@ -655,37 +708,33 @@ double jitter_request(int *status){ rval = num_valid ? rval / num_valid : -1.0; close(conn); - if(peers!=NULL) free(peers); + if (peers != NULL) { + free(peers); + } /* If we return -1.0, it means no synchronization source was found */ return rval; } -int process_arguments(int argc, char **argv){ +int process_arguments(int argc, char **argv) { int c; - int option=0; + int option = 0; static struct option longopts[] = { - {"version", no_argument, 0, 'V'}, - {"help", no_argument, 0, 'h'}, - {"verbose", no_argument, 0, 'v'}, - {"use-ipv4", no_argument, 0, '4'}, - {"use-ipv6", no_argument, 0, '6'}, - {"warning", required_argument, 0, 'w'}, - {"critical", required_argument, 0, 'c'}, - {"jwarn", required_argument, 0, 'j'}, - {"jcrit", required_argument, 0, 'k'}, - {"timeout", required_argument, 0, 't'}, - {"hostname", required_argument, 0, 'H'}, - {0, 0, 0, 0} - }; - - - if (argc < 2) - usage ("\n"); + {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, {"warning", required_argument, 0, 'w'}, + {"critical", required_argument, 0, 'c'}, {"jwarn", required_argument, 0, 'j'}, + {"jcrit", required_argument, 0, 'k'}, {"timeout", required_argument, 0, 't'}, + {"hostname", required_argument, 0, 'H'}, {0, 0, 0, 0}}; + + if (argc < 2) { + usage("\n"); + } while (1) { - c = getopt_long (argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); - if (c == -1 || c == EOF || c == 1) + c = getopt_long(argc, argv, "Vhv46w:c:j:k:t:H:", longopts, &option); + if (c == -1 || c == EOF || c == 1) { break; + } switch (c) { case 'h': @@ -716,12 +765,13 @@ int process_arguments(int argc, char **argv){ jcrit = optarg; break; case 'H': - if(!is_host(optarg)) + if (!is_host(optarg)) { usage2(_("Invalid hostname/address"), optarg); + } server_address = strdup(optarg); break; case 't': - socket_timeout=atoi(optarg); + socket_timeout = atoi(optarg); break; case '4': address_family = AF_INET; @@ -730,64 +780,59 @@ int process_arguments(int argc, char **argv){ #ifdef USE_IPV6 address_family = AF_INET6; #else - usage4 (_("IPv6 support not available")); + usage4(_("IPv6 support not available")); #endif break; case '?': /* print short usage statement if args not parsable */ - usage5 (); + usage5(); break; } } - if(server_address == NULL){ + if (server_address == NULL) { usage4(_("Hostname was not supplied")); } return 0; } -char *perfd_offset (double offset) -{ - return fperfdata ("offset", offset, "s", - true, offset_thresholds->warning->end, - true, offset_thresholds->critical->end, - false, 0, false, 0); +char *perfd_offset(double offset) { + return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, + offset_thresholds->critical->end, false, 0, false, 0); } -char *perfd_jitter (double jitter) -{ - return fperfdata ("jitter", jitter, "s", - do_jitter, jitter_thresholds->warning->end, - do_jitter, jitter_thresholds->critical->end, - true, 0, false, 0); +char *perfd_jitter(double jitter) { + return fperfdata("jitter", jitter, "s", do_jitter, jitter_thresholds->warning->end, do_jitter, + jitter_thresholds->critical->end, true, 0, false, 0); } -int main(int argc, char *argv[]){ +int main(int argc, char *argv[]) { int result, offset_result, jitter_result; - double offset=0, jitter=0; + double offset = 0, jitter = 0; char *result_line, *perfdata_line; - setlocale (LC_ALL, ""); - bindtextdomain (PACKAGE, LOCALEDIR); - textdomain (PACKAGE); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); result = offset_result = jitter_result = STATE_OK; /* 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")); + if (process_arguments(argc, argv) == ERROR) { + usage4(_("Could not parse arguments")); + } set_thresholds(&offset_thresholds, owarn, ocrit); set_thresholds(&jitter_thresholds, jwarn, jcrit); /* initialize alarm signal handling */ - signal (SIGALRM, socket_timeout_alarm_handler); + signal(SIGALRM, socket_timeout_alarm_handler); /* set socket timeout */ - alarm (socket_timeout); + alarm(socket_timeout); offset = offset_request(server_address, &offset_result); /* check_ntp used to always return CRITICAL if offset_result == STATE_UNKNOWN. @@ -803,31 +848,32 @@ int main(int argc, char *argv[]){ * servers recognize. Trying to check the jitter on OpenNTPD * (for example) will result in an error */ - if(do_jitter){ - jitter=jitter_request(&jitter_result); + if (do_jitter) { + jitter = jitter_request(&jitter_result); result = max_state_alt(result, get_status(jitter, jitter_thresholds)); /* -1 indicates that we couldn't calculate the jitter * Only overrides STATE_OK from the offset */ - if(jitter == -1.0 && result == STATE_OK) + if (jitter == -1.0 && result == STATE_OK) { result = STATE_UNKNOWN; + } } result = max_state_alt(result, jitter_result); switch (result) { - case STATE_CRITICAL : - xasprintf(&result_line, _("NTP CRITICAL:")); - break; - case STATE_WARNING : - xasprintf(&result_line, _("NTP WARNING:")); - break; - case STATE_OK : - xasprintf(&result_line, _("NTP OK:")); - break; - default : - xasprintf(&result_line, _("NTP UNKNOWN:")); - break; + case STATE_CRITICAL: + xasprintf(&result_line, _("NTP CRITICAL:")); + break; + case STATE_WARNING: + xasprintf(&result_line, _("NTP WARNING:")); + break; + case STATE_OK: + xasprintf(&result_line, _("NTP OK:")); + break; + default: + xasprintf(&result_line, _("NTP UNKNOWN:")); + break; } - if(offset_result == STATE_UNKNOWN){ + if (offset_result == STATE_UNKNOWN) { xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); xasprintf(&perfdata_line, ""); } else { @@ -836,41 +882,41 @@ int main(int argc, char *argv[]){ } if (do_jitter) { xasprintf(&result_line, "%s, jitter=%f", result_line, jitter); - xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); + xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(jitter)); } printf("%s|%s\n", result_line, perfdata_line); - if(server_address!=NULL) free(server_address); + if (server_address != NULL) { + free(server_address); + } return result; } - - -void print_help(void){ +void print_help(void) { print_revision(progname, NP_VERSION); - printf ("Copyright (c) 2006 Sean Finney\n"); - printf (COPYRIGHT, copyright, email); + printf("Copyright (c) 2006 Sean Finney\n"); + printf(COPYRIGHT, copyright, email); - printf ("%s\n", _("This plugin checks the selected ntp server")); + printf("%s\n", _("This plugin checks the selected ntp server")); - printf ("\n\n"); + printf("\n\n"); print_usage(); - printf (UT_HELP_VRSN); - printf (UT_EXTRA_OPTS); - printf (UT_HOST_PORT, 'p', "123"); - printf (UT_IPv46); - printf (" %s\n", "-w, --warning=THRESHOLD"); - printf (" %s\n", _("Offset to result in warning status (seconds)")); - printf (" %s\n", "-c, --critical=THRESHOLD"); - printf (" %s\n", _("Offset to result in critical status (seconds)")); - printf (" %s\n", "-j, --jwarn=THRESHOLD"); - printf (" %s\n", _("Warning threshold for jitter")); - printf (" %s\n", "-k, --jcrit=THRESHOLD"); - printf (" %s\n", _("Critical threshold for jitter")); - printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); - printf (UT_VERBOSE); + printf(UT_HELP_VRSN); + printf(UT_EXTRA_OPTS); + printf(UT_HOST_PORT, 'p', "123"); + printf(UT_IPv46); + printf(" %s\n", "-w, --warning=THRESHOLD"); + printf(" %s\n", _("Offset to result in warning status (seconds)")); + printf(" %s\n", "-c, --critical=THRESHOLD"); + printf(" %s\n", _("Offset to result in critical status (seconds)")); + printf(" %s\n", "-j, --jwarn=THRESHOLD"); + printf(" %s\n", _("Warning threshold for jitter")); + printf(" %s\n", "-k, --jcrit=THRESHOLD"); + printf(" %s\n", _("Critical threshold for jitter")); + printf(UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT); + printf(UT_VERBOSE); printf("\n"); printf("%s\n", _("Notes:")); @@ -881,21 +927,21 @@ void print_help(void){ printf(" %s\n", _("Normal offset check:")); printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1")); printf("\n"); - printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); + printf(" %s\n", + _("Check jitter too, avoiding critical notifications if jitter isn't available")); printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); printf(" %s\n", ("./check_ntp -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); - printf (UT_SUPPORT); + printf(UT_SUPPORT); - printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); - printf ("%s\n\n", _("check_ntp_time instead.")); + printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); + printf("%s\n\n", _("check_ntp_time instead.")); } -void -print_usage(void) -{ - printf ("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); - printf ("%s\n\n", _("check_ntp_time instead.")); - printf ("%s\n", _("Usage:")); - printf(" %s -H [-w ] [-c ] [-j ] [-k ] [-4|-6] [-v verbose]\n", progname); +void print_usage(void) { + printf("%s\n", _("WARNING: check_ntp is deprecated. Please use check_ntp_peer or")); + printf("%s\n\n", _("check_ntp_time instead.")); + printf("%s\n", _("Usage:")); + printf(" %s -H [-w ] [-c ] [-j ] [-k ] [-4|-6] [-v verbose]\n", + progname); } diff --git a/plugins/check_ntp_peer.c b/plugins/check_ntp_peer.c index 5c4ff386..24d1c9b5 100644 --- a/plugins/check_ntp_peer.c +++ b/plugins/check_ntp_peer.c @@ -83,9 +83,9 @@ typedef struct { /* bits 1,2 are the leap indicator */ #define LI_MASK 0xc0 #define LI(x) ((x & LI_MASK) >> 6) -#define LI_SET(x, y) \ - do { \ - x |= ((y << 6) & LI_MASK); \ +#define LI_SET(x, y) \ + do { \ + x |= ((y << 6) & LI_MASK); \ } while (0) /* and these are the values of the leap indicator */ #define LI_NOWARNING 0x00 @@ -95,17 +95,17 @@ typedef struct { /* bits 3,4,5 are the ntp version */ #define VN_MASK 0x38 #define VN(x) ((x & VN_MASK) >> 3) -#define VN_SET(x, y) \ - do { \ - x |= ((y << 3) & VN_MASK); \ +#define VN_SET(x, y) \ + do { \ + x |= ((y << 3) & VN_MASK); \ } while (0) #define VN_RESERVED 0x02 /* bits 6,7,8 are the ntp mode */ #define MODE_MASK 0x07 #define MODE(x) (x & MODE_MASK) -#define MODE_SET(x, y) \ - do { \ - x |= (y & MODE_MASK); \ +#define MODE_SET(x, y) \ + do { \ + x |= (y & MODE_MASK); \ } while (0) /* here are some values */ #define MODE_CLIENT 0x03 @@ -117,9 +117,9 @@ typedef struct { #define REM_MORE 0x20 /* In control message, bits 11 - 15 are opcode */ #define OP_MASK 0x1f -#define OP_SET(x, y) \ - do { \ - x |= (y & OP_MASK); \ +#define OP_SET(x, y) \ + do { \ + x |= (y & OP_MASK); \ } while (0) #define OP_READSTAT 0x01 #define OP_READVAR 0x02 @@ -132,18 +132,19 @@ typedef struct { /* NTP control message header is 12 bytes, plus any data in the data * field, plus null padding to the nearest 32-bit boundary per rfc. */ -#define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) +#define SIZEOF_NTPCM(m) \ + (12 + ntohs(m.count) + ((ntohs(m.count) % 4) ? 4 - (ntohs(m.count) % 4) : 0)) /* finally, a little helper or two for debugging: */ -#define DBG(x) \ - do { \ - if (verbose > 1) { \ - x; \ - } \ +#define DBG(x) \ + do { \ + if (verbose > 1) { \ + x; \ + } \ } while (0); -#define PRINTSOCKADDR(x) \ - do { \ - printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ +#define PRINTSOCKADDR(x) \ + do { \ + printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ } while (0); void print_ntp_control_message(const ntp_control_message *message) { @@ -360,7 +361,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { if (req.op & REM_ERROR) { if (strstr(getvar, "jitter")) { if (verbose) { - printf("The command failed. This is usually caused by servers refusing the 'jitter'\nvariable. Restarting with " + printf("The command failed. This is usually caused by servers refusing the " + "'jitter'\nvariable. Restarting with " "'dispersion'...\n"); } getvar = "stratum,offset,dispersion"; @@ -404,7 +406,8 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { if (verbose) { printf("%.10g\n", tmp_offset); } - if (result.offset_result == STATE_UNKNOWN || fabs(tmp_offset) < fabs(result.offset)) { + if (result.offset_result == STATE_UNKNOWN || + fabs(tmp_offset) < fabs(result.offset)) { result.offset = tmp_offset; result.offset_result = STATE_OK; } else { @@ -416,10 +419,12 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { if (config.do_jitter) { /* get the jitter */ if (verbose) { - printf("parsing %s from peer %.2x: ", strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", + printf("parsing %s from peer %.2x: ", + strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter", ntohs(peers[i].assoc)); } - value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" : "jitter"); + value = np_extract_ntpvar(data, strstr(getvar, "dispersion") != NULL ? "dispersion" + : "jitter"); nptr = NULL; /* Convert the value if we have one */ if (value != NULL) { @@ -471,12 +476,15 @@ ntp_request_result ntp_request(const check_ntp_peer_config config) { check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { static struct option longopts[] = { - {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, - {"use-ipv4", no_argument, 0, '4'}, {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, - {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, {"swarn", required_argument, 0, 'W'}, - {"scrit", required_argument, 0, 'C'}, {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, - {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, {"timeout", required_argument, 0, 't'}, - {"hostname", required_argument, 0, 'H'}, {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; + {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, {"use-ipv4", no_argument, 0, '4'}, + {"use-ipv6", no_argument, 0, '6'}, {"quiet", no_argument, 0, 'q'}, + {"warning", required_argument, 0, 'w'}, {"critical", required_argument, 0, 'c'}, + {"swarn", required_argument, 0, 'W'}, {"scrit", required_argument, 0, 'C'}, + {"jwarn", required_argument, 0, 'j'}, {"jcrit", required_argument, 0, 'k'}, + {"twarn", required_argument, 0, 'm'}, {"tcrit", required_argument, 0, 'n'}, + {"timeout", required_argument, 0, 't'}, {"hostname", required_argument, 0, 'H'}, + {"port", required_argument, 0, 'p'}, {0, 0, 0, 0}}; if (argc < 2) { usage("\n"); @@ -489,7 +497,8 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { while (true) { int option = 0; - int option_char = getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); + int option_char = + getopt_long(argc, argv, "Vhv46qw:c:W:C:j:k:m:n:t:H:p:", longopts, &option); if (option_char == -1 || option_char == EOF || option_char == 1) { break; } @@ -581,22 +590,24 @@ check_ntp_peer_config_wrapper process_arguments(int argc, char **argv) { } char *perfd_offset(double offset, thresholds *offset_thresholds) { - return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, - 0); + return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, + offset_thresholds->critical->end, false, 0, false, 0); } char *perfd_jitter(double jitter, bool do_jitter, thresholds *jitter_thresholds) { - return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, jitter_thresholds->critical->end, true, 0, - false, 0); + return fperfdata("jitter", jitter, "", do_jitter, jitter_thresholds->warning->end, do_jitter, + jitter_thresholds->critical->end, true, 0, false, 0); } char *perfd_stratum(int stratum, bool do_stratum, thresholds *stratum_thresholds) { - return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, do_stratum, - (int)stratum_thresholds->critical->end, true, 0, true, 16); + return perfdata("stratum", stratum, "", do_stratum, (int)stratum_thresholds->warning->end, + do_stratum, (int)stratum_thresholds->critical->end, true, 0, true, 16); } -char *perfd_truechimers(int num_truechimers, const bool do_truechimers, thresholds *truechimer_thresholds) { - return perfdata("truechimers", num_truechimers, "", do_truechimers, (int)truechimer_thresholds->warning->end, do_truechimers, +char *perfd_truechimers(int num_truechimers, const bool do_truechimers, + thresholds *truechimer_thresholds) { + return perfdata("truechimers", num_truechimers, "", do_truechimers, + (int)truechimer_thresholds->warning->end, do_truechimers, (int)truechimer_thresholds->critical->end, true, 0, false, 0); } @@ -686,9 +697,11 @@ int main(int argc, char *argv[]) { xasprintf(&result_line, "%s %s", result_line, _("Offset unknown")); xasprintf(&perfdata_line, ""); } else if (oresult == STATE_WARNING) { - xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), ntp_res.offset); + xasprintf(&result_line, "%s %s %.10g secs (WARNING)", result_line, _("Offset"), + ntp_res.offset); } else if (oresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), ntp_res.offset); + xasprintf(&result_line, "%s %s %.10g secs (CRITICAL)", result_line, _("Offset"), + ntp_res.offset); } else { xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), ntp_res.offset); } @@ -702,7 +715,8 @@ int main(int argc, char *argv[]) { } else { xasprintf(&result_line, "%s, jitter=%f", result_line, ntp_res.jitter); } - xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds)); + xasprintf(&perfdata_line, "%s %s", perfdata_line, + perfd_jitter(ntp_res.jitter, config.do_jitter, config.jitter_thresholds)); } if (config.do_stratum) { @@ -713,19 +727,23 @@ int main(int argc, char *argv[]) { } else { xasprintf(&result_line, "%s, stratum=%li", result_line, ntp_res.stratum); } - xasprintf(&perfdata_line, "%s %s", perfdata_line, perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds)); + xasprintf(&perfdata_line, "%s %s", perfdata_line, + perfd_stratum(ntp_res.stratum, config.do_stratum, config.stratum_thresholds)); } if (config.do_truechimers) { if (tresult == STATE_WARNING) { - xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, ntp_res.num_truechimers); + xasprintf(&result_line, "%s, truechimers=%i (WARNING)", result_line, + ntp_res.num_truechimers); } else if (tresult == STATE_CRITICAL) { - xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, ntp_res.num_truechimers); + xasprintf(&result_line, "%s, truechimers=%i (CRITICAL)", result_line, + ntp_res.num_truechimers); } else { xasprintf(&result_line, "%s, truechimers=%i", result_line, ntp_res.num_truechimers); } xasprintf(&perfdata_line, "%s %s", perfdata_line, - perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers, config.truechimer_thresholds)); + perfd_truechimers(ntp_res.num_truechimers, config.do_truechimers, + config.truechimer_thresholds)); } printf("%s|%s\n", result_line, perfdata_line); @@ -753,7 +771,8 @@ void print_help(void) { printf(UT_IPv46); printf(UT_HOST_PORT, 'p', "123"); printf(" %s\n", "-q, --quiet"); - printf(" %s\n", _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); + printf(" %s\n", + _("Returns UNKNOWN instead of CRITICAL or WARNING if server isn't synchronized")); printf(" %s\n", "-w, --warning=THRESHOLD"); printf(" %s\n", _("Offset to result in warning status (seconds)")); printf(" %s\n", "-c, --critical=THRESHOLD"); @@ -790,7 +809,8 @@ void print_help(void) { printf(" %s\n", _("Simple NTP server check:")); printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1")); printf("\n"); - printf(" %s\n", _("Check jitter too, avoiding critical notifications if jitter isn't available")); + printf(" %s\n", + _("Check jitter too, avoiding critical notifications if jitter isn't available")); printf(" %s\n", _("(See Notes above for more details on thresholds formats):")); printf(" %s\n", ("./check_ntp_peer -H ntpserv -w 0.5 -c 1 -j -1:100 -k -1:200")); printf("\n"); diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c index 31162883..ad69b804 100644 --- a/plugins/check_ntp_time.c +++ b/plugins/check_ntp_time.c @@ -93,9 +93,9 @@ typedef struct { /* bits 1,2 are the leap indicator */ #define LI_MASK 0xc0 #define LI(x) ((x & LI_MASK) >> 6) -#define LI_SET(x, y) \ - do { \ - x |= ((y << 6) & LI_MASK); \ +#define LI_SET(x, y) \ + do { \ + x |= ((y << 6) & LI_MASK); \ } while (0) /* and these are the values of the leap indicator */ #define LI_NOWARNING 0x00 @@ -105,17 +105,17 @@ typedef struct { /* bits 3,4,5 are the ntp version */ #define VN_MASK 0x38 #define VN(x) ((x & VN_MASK) >> 3) -#define VN_SET(x, y) \ - do { \ - x |= ((y << 3) & VN_MASK); \ +#define VN_SET(x, y) \ + do { \ + x |= ((y << 3) & VN_MASK); \ } while (0) #define VN_RESERVED 0x02 /* bits 6,7,8 are the ntp mode */ #define MODE_MASK 0x07 #define MODE(x) (x & MODE_MASK) -#define MODE_SET(x, y) \ - do { \ - x |= (y & MODE_MASK); \ +#define MODE_SET(x, y) \ + do { \ + x |= (y & MODE_MASK); \ } while (0) /* here are some values */ #define MODE_CLIENT 0x03 @@ -127,9 +127,9 @@ typedef struct { #define REM_MORE 0x20 /* In control message, bits 11 - 15 are opcode */ #define OP_MASK 0x1f -#define OP_SET(x, y) \ - do { \ - x |= (y & OP_MASK); \ +#define OP_SET(x, y) \ + do { \ + x |= (y & OP_MASK); \ } while (0) #define OP_READSTAT 0x01 #define OP_READVAR 0x02 @@ -163,32 +163,34 @@ typedef struct { #define NTP32asDOUBLE(x) (ntohs(L16(x)) + ((double)ntohs(R16(x)) / 65536.0)) /* likewise for a 64-bit ntp fp number */ -#define NTP64asDOUBLE(n) \ - (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) : 0) +#define NTP64asDOUBLE(n) \ + (double)(((uint64_t)n) ? (ntohl(L32(n)) - EPOCHDIFF) + \ + (.00000001 * (0.5 + (double)(ntohl(R32(n)) / 42.94967296))) \ + : 0) /* convert a struct timeval to a double */ #define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec)) /* convert an ntp 64-bit fp number to a struct timeval */ -#define NTP64toTV(n, t) \ - do { \ - if (!n) \ - t.tv_sec = t.tv_usec = 0; \ - else { \ - t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ - t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ - } \ +#define NTP64toTV(n, t) \ + do { \ + if (!n) \ + t.tv_sec = t.tv_usec = 0; \ + else { \ + t.tv_sec = ntohl(L32(n)) - EPOCHDIFF; \ + t.tv_usec = (int)(0.5 + (double)(ntohl(R32(n)) / 4294.967296)); \ + } \ } while (0) /* convert a struct timeval to an ntp 64-bit fp number */ -#define TVtoNTP64(t, n) \ - do { \ - if (!t.tv_usec && !t.tv_sec) \ - n = 0x0UL; \ - else { \ - L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ - R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ - } \ +#define TVtoNTP64(t, n) \ + do { \ + if (!t.tv_usec && !t.tv_sec) \ + n = 0x0UL; \ + else { \ + L32(n) = htonl(t.tv_sec + EPOCHDIFF); \ + R32(n) = htonl((uint64_t)((4294.967296 * t.tv_usec) + .5)); \ + } \ } while (0) /* NTP control message header is 12 bytes, plus any data in the data @@ -197,15 +199,15 @@ typedef struct { #define SIZEOF_NTPCM(m) (12 + ntohs(m.count) + ((m.count) ? 4 - (ntohs(m.count) % 4) : 0)) /* finally, a little helper or two for debugging: */ -#define DBG(x) \ - do { \ - if (verbose > 1) { \ - x; \ - } \ +#define DBG(x) \ + do { \ + if (verbose > 1) { \ + x; \ + } \ } while (0); -#define PRINTSOCKADDR(x) \ - do { \ - printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ +#define PRINTSOCKADDR(x) \ + do { \ + printf("%u.%u.%u.%u", (x >> 24) & 0xff, (x >> 16) & 0xff, (x >> 8) & 0xff, x & 0xff); \ } while (0); /* calculate the offset of the local clock */ @@ -358,7 +360,8 @@ double offset_request(const char *host, const char *port, mp_state_enum *status, die(STATE_UNKNOWN, "can not allocate socket array"); } - ntp_server_results *servers = (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); + ntp_server_results *servers = + (ntp_server_results *)malloc(sizeof(ntp_server_results) * num_hosts); if (servers == NULL) { die(STATE_UNKNOWN, "can not allocate server array"); } @@ -585,8 +588,8 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) { } char *perfd_offset(double offset, thresholds *offset_thresholds) { - return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, offset_thresholds->critical->end, false, 0, false, - 0); + return fperfdata("offset", offset, "s", true, offset_thresholds->warning->end, true, + offset_thresholds->critical->end, false, 0, false, 0); } int main(int argc, char *argv[]) { @@ -613,7 +616,8 @@ int main(int argc, char *argv[]) { mp_state_enum offset_result = STATE_OK; mp_state_enum result = STATE_OK; - double offset = offset_request(config.server_address, config.port, &offset_result, config.time_offset); + double offset = + offset_request(config.server_address, config.port, &offset_result, config.time_offset); if (offset_result == STATE_UNKNOWN) { result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL); } else { @@ -701,5 +705,6 @@ void print_help(void) { void print_usage(void) { printf("%s\n", _("Usage:")); - printf(" %s -H [-4|-6] [-w ] [-c ] [-v verbose] [-o