diff options
| author | Andreas Baumann <mail@andreasbaumann.cc> | 2017-04-14 21:10:40 +0200 |
|---|---|---|
| committer | Sven Nierlein <sven@nierlein.de> | 2018-10-22 16:30:31 +0200 |
| commit | 9960c56e5e09caf33a8010e52cb32a931fb3ab2a (patch) | |
| tree | 584c3efc2dc5b7f6870894ec7f4cac0662d51753 /plugins/check_curl.c | |
| parent | bbec77c7ecbc6ecaac06d2c8845f83a22546f273 (diff) | |
| download | monitoring-plugins-9960c56e5e09caf33a8010e52cb32a931fb3ab2a.tar.gz | |
added -M<m> age option for document age, using picohttpparser from h2o (maybe handy
later to make a more robust header condition checker?)
Diffstat (limited to 'plugins/check_curl.c')
| -rw-r--r-- | plugins/check_curl.c | 203 |
1 files changed, 198 insertions, 5 deletions
diff --git a/plugins/check_curl.c b/plugins/check_curl.c index 4129acca..7576b2df 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c | |||
| @@ -37,6 +37,8 @@ const char *progname = "check_curl"; | |||
| 37 | const char *copyright = "2006-2017"; | 37 | const char *copyright = "2006-2017"; |
| 38 | const char *email = "devel@monitoring-plugins.org"; | 38 | const char *email = "devel@monitoring-plugins.org"; |
| 39 | 39 | ||
| 40 | #include <ctype.h> | ||
| 41 | |||
| 40 | #include "common.h" | 42 | #include "common.h" |
| 41 | #include "utils.h" | 43 | #include "utils.h" |
| 42 | 44 | ||
| @@ -47,6 +49,8 @@ const char *email = "devel@monitoring-plugins.org"; | |||
| 47 | #include "curl/curl.h" | 49 | #include "curl/curl.h" |
| 48 | #include "curl/easy.h" | 50 | #include "curl/easy.h" |
| 49 | 51 | ||
| 52 | #include "picohttpparser.h" | ||
| 53 | |||
| 50 | #define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch)) | 54 | #define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch)) |
| 51 | 55 | ||
| 52 | #define DEFAULT_BUFFER_SIZE 2048 | 56 | #define DEFAULT_BUFFER_SIZE 2048 |
| @@ -73,7 +77,7 @@ typedef struct { | |||
| 73 | int http_code; /* HTTP return code as in RFC 2145 */ | 77 | int http_code; /* HTTP return code as in RFC 2145 */ |
| 74 | int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see | 78 | int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see |
| 75 | * http://support.microsoft.com/kb/318380/en-us */ | 79 | * http://support.microsoft.com/kb/318380/en-us */ |
| 76 | char *msg; /* the human readable message */ | 80 | const char *msg; /* the human readable message */ |
| 77 | char *first_line; /* a copy of the first line */ | 81 | char *first_line; /* a copy of the first line */ |
| 78 | } curlhelp_statusline; | 82 | } curlhelp_statusline; |
| 79 | 83 | ||
| @@ -142,6 +146,7 @@ char *client_privkey = NULL; | |||
| 142 | char *ca_cert = NULL; | 146 | char *ca_cert = NULL; |
| 143 | X509 *cert = NULL; | 147 | X509 *cert = NULL; |
| 144 | int no_body = FALSE; | 148 | int no_body = FALSE; |
| 149 | int maximum_age = -1; | ||
| 145 | int address_family = AF_UNSPEC; | 150 | int address_family = AF_UNSPEC; |
| 146 | 151 | ||
| 147 | int process_arguments (int, char**); | 152 | int process_arguments (int, char**); |
| @@ -156,6 +161,9 @@ void curlhelp_freebuffer (curlhelp_curlbuf*); | |||
| 156 | int curlhelp_parse_statusline (const char*, curlhelp_statusline *); | 161 | int curlhelp_parse_statusline (const char*, curlhelp_statusline *); |
| 157 | void curlhelp_free_statusline (curlhelp_statusline *); | 162 | void curlhelp_free_statusline (curlhelp_statusline *); |
| 158 | char *perfd_time_ssl (double microsec); | 163 | char *perfd_time_ssl (double microsec); |
| 164 | char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header); | ||
| 165 | static time_t parse_time_string (const char *string); | ||
| 166 | int check_document_dates (const curlhelp_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]); | ||
| 159 | 167 | ||
| 160 | void remove_newlines (char *); | 168 | void remove_newlines (char *); |
| 161 | void test_file (char *); | 169 | void test_file (char *); |
| @@ -391,7 +399,7 @@ check_http (void) | |||
| 391 | /* Curl errors, result in critical Nagios state */ | 399 | /* Curl errors, result in critical Nagios state */ |
| 392 | if (res != CURLE_OK) { | 400 | if (res != CURLE_OK) { |
| 393 | remove_newlines (errbuf); | 401 | remove_newlines (errbuf); |
| 394 | snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s\n"), | 402 | snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"), |
| 395 | server_port, res, curl_easy_strerror(res)); | 403 | server_port, res, curl_easy_strerror(res)); |
| 396 | die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); | 404 | die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); |
| 397 | } | 405 | } |
| @@ -506,6 +514,10 @@ check_http (void) | |||
| 506 | status_line.http_code, status_line.msg, code); | 514 | status_line.http_code, status_line.msg, code); |
| 507 | } | 515 | } |
| 508 | 516 | ||
| 517 | if (maximum_age >= 0) { | ||
| 518 | result = max_state_alt(check_document_dates(&header_buf, &msg), result); | ||
| 519 | } | ||
| 520 | |||
| 509 | /* Page and Header content checks go here */ | 521 | /* Page and Header content checks go here */ |
| 510 | 522 | ||
| 511 | if (strlen (header_expect)) { | 523 | if (strlen (header_expect)) { |
| @@ -638,6 +650,7 @@ process_arguments (int argc, char **argv) | |||
| 638 | {"useragent", required_argument, 0, 'A'}, | 650 | {"useragent", required_argument, 0, 'A'}, |
| 639 | {"header", required_argument, 0, 'k'}, | 651 | {"header", required_argument, 0, 'k'}, |
| 640 | {"no-body", no_argument, 0, 'N'}, | 652 | {"no-body", no_argument, 0, 'N'}, |
| 653 | {"max-age", required_argument, 0, 'M'}, | ||
| 641 | {"content-type", required_argument, 0, 'T'}, | 654 | {"content-type", required_argument, 0, 'T'}, |
| 642 | {"pagesize", required_argument, 0, 'm'}, | 655 | {"pagesize", required_argument, 0, 'm'}, |
| 643 | {"invert-regex", no_argument, NULL, INVERT_REGEX}, | 656 | {"invert-regex", no_argument, NULL, INVERT_REGEX}, |
| @@ -665,7 +678,7 @@ process_arguments (int argc, char **argv) | |||
| 665 | } | 678 | } |
| 666 | 679 | ||
| 667 | while (1) { | 680 | while (1) { |
| 668 | c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:p:d:e:s:R:r:u:f:C:J:K:S::m:NE", longopts, &option); | 681 | c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:p:d:e:s:R:r:u:f:C:J:K:S::m:M:NE", longopts, &option); |
| 669 | if (c == -1 || c == EOF || c == 1) | 682 | if (c == -1 || c == EOF || c == 1) |
| 670 | break; | 683 | break; |
| 671 | 684 | ||
| @@ -962,6 +975,26 @@ process_arguments (int argc, char **argv) | |||
| 962 | case 'N': /* no-body */ | 975 | case 'N': /* no-body */ |
| 963 | no_body = TRUE; | 976 | no_body = TRUE; |
| 964 | break; | 977 | break; |
| 978 | case 'M': /* max-age */ | ||
| 979 | { | ||
| 980 | int L = strlen(optarg); | ||
| 981 | if (L && optarg[L-1] == 'm') | ||
| 982 | maximum_age = atoi (optarg) * 60; | ||
| 983 | else if (L && optarg[L-1] == 'h') | ||
| 984 | maximum_age = atoi (optarg) * 60 * 60; | ||
| 985 | else if (L && optarg[L-1] == 'd') | ||
| 986 | maximum_age = atoi (optarg) * 60 * 60 * 24; | ||
| 987 | else if (L && (optarg[L-1] == 's' || | ||
| 988 | isdigit (optarg[L-1]))) | ||
| 989 | maximum_age = atoi (optarg); | ||
| 990 | else { | ||
| 991 | fprintf (stderr, "unparsable max-age: %s\n", optarg); | ||
| 992 | exit (STATE_WARNING); | ||
| 993 | } | ||
| 994 | if (verbose >= 2) | ||
| 995 | printf ("* Maximal age of document set to %d seconds\n", maximum_age); | ||
| 996 | } | ||
| 997 | break; | ||
| 965 | case 'E': /* show extended perfdata */ | 998 | case 'E': /* show extended perfdata */ |
| 966 | show_extended_perfdata = TRUE; | 999 | show_extended_perfdata = TRUE; |
| 967 | break; | 1000 | break; |
| @@ -1090,6 +1123,9 @@ print_help (void) | |||
| 1090 | printf (" %s\n", "-N, --no-body"); | 1123 | printf (" %s\n", "-N, --no-body"); |
| 1091 | printf (" %s\n", _("Don't wait for document body: stop reading after headers.")); | 1124 | printf (" %s\n", _("Don't wait for document body: stop reading after headers.")); |
| 1092 | printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)")); | 1125 | printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)")); |
| 1126 | printf (" %s\n", "-M, --max-age=SECONDS"); | ||
| 1127 | printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of")); | ||
| 1128 | printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.")); | ||
| 1093 | printf (" %s\n", "-T, --content-type=STRING"); | 1129 | printf (" %s\n", "-T, --content-type=STRING"); |
| 1094 | printf (" %s\n", _("specify Content-Type header media type when POSTing\n")); | 1130 | printf (" %s\n", _("specify Content-Type header media type when POSTing\n")); |
| 1095 | printf (" %s\n", "-l, --linespan"); | 1131 | printf (" %s\n", "-l, --linespan"); |
| @@ -1181,7 +1217,7 @@ print_usage (void) | |||
| 1181 | printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-E] [-a auth]\n"); | 1217 | printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-E] [-a auth]\n"); |
| 1182 | printf (" [-f <ok|warning|critcal|follow>]\n"); | 1218 | printf (" [-f <ok|warning|critcal|follow>]\n"); |
| 1183 | printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); | 1219 | printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n"); |
| 1184 | printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N]\n"); | 1220 | printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n"); |
| 1185 | printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n"); | 1221 | printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n"); |
| 1186 | printf (" [-T <content-type>] [-j method]\n", progname); | 1222 | printf (" [-T <content-type>] [-j method]\n", progname); |
| 1187 | printf ("\n"); | 1223 | printf ("\n"); |
| @@ -1351,7 +1387,164 @@ remove_newlines (char *s) | |||
| 1351 | *p = ' '; | 1387 | *p = ' '; |
| 1352 | } | 1388 | } |
| 1353 | 1389 | ||
| 1354 | char *perfd_time_ssl (double elapsed_time_ssl) | 1390 | char * |
| 1391 | perfd_time_ssl (double elapsed_time_ssl) | ||
| 1355 | { | 1392 | { |
| 1356 | return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0); | 1393 | return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0); |
| 1357 | } | 1394 | } |
| 1395 | |||
| 1396 | char * | ||
| 1397 | get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header) | ||
| 1398 | { | ||
| 1399 | for( int i = 0; i < nof_headers; i++ ) { | ||
| 1400 | if( strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) { | ||
| 1401 | return strndup( headers[i].value, headers[i].value_len ); | ||
| 1402 | } | ||
| 1403 | } | ||
| 1404 | return NULL; | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | static time_t | ||
| 1408 | parse_time_string (const char *string) | ||
| 1409 | { | ||
| 1410 | struct tm tm; | ||
| 1411 | time_t t; | ||
| 1412 | memset (&tm, 0, sizeof(tm)); | ||
| 1413 | |||
| 1414 | /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */ | ||
| 1415 | |||
| 1416 | if (isupper (string[0]) && /* Tue */ | ||
| 1417 | islower (string[1]) && | ||
| 1418 | islower (string[2]) && | ||
| 1419 | ',' == string[3] && | ||
| 1420 | ' ' == string[4] && | ||
| 1421 | (isdigit(string[5]) || string[5] == ' ') && /* 25 */ | ||
| 1422 | isdigit (string[6]) && | ||
| 1423 | ' ' == string[7] && | ||
| 1424 | isupper (string[8]) && /* Dec */ | ||
| 1425 | islower (string[9]) && | ||
| 1426 | islower (string[10]) && | ||
| 1427 | ' ' == string[11] && | ||
| 1428 | isdigit (string[12]) && /* 2001 */ | ||
| 1429 | isdigit (string[13]) && | ||
| 1430 | isdigit (string[14]) && | ||
| 1431 | isdigit (string[15]) && | ||
| 1432 | ' ' == string[16] && | ||
| 1433 | isdigit (string[17]) && /* 02: */ | ||
| 1434 | isdigit (string[18]) && | ||
| 1435 | ':' == string[19] && | ||
| 1436 | isdigit (string[20]) && /* 59: */ | ||
| 1437 | isdigit (string[21]) && | ||
| 1438 | ':' == string[22] && | ||
| 1439 | isdigit (string[23]) && /* 03 */ | ||
| 1440 | isdigit (string[24]) && | ||
| 1441 | ' ' == string[25] && | ||
| 1442 | 'G' == string[26] && /* GMT */ | ||
| 1443 | 'M' == string[27] && /* GMT */ | ||
| 1444 | 'T' == string[28]) { | ||
| 1445 | |||
| 1446 | tm.tm_sec = 10 * (string[23]-'0') + (string[24]-'0'); | ||
| 1447 | tm.tm_min = 10 * (string[20]-'0') + (string[21]-'0'); | ||
| 1448 | tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0'); | ||
| 1449 | tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0'); | ||
| 1450 | tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 : | ||
| 1451 | !strncmp (string+8, "Feb", 3) ? 1 : | ||
| 1452 | !strncmp (string+8, "Mar", 3) ? 2 : | ||
| 1453 | !strncmp (string+8, "Apr", 3) ? 3 : | ||
| 1454 | !strncmp (string+8, "May", 3) ? 4 : | ||
| 1455 | !strncmp (string+8, "Jun", 3) ? 5 : | ||
| 1456 | !strncmp (string+8, "Jul", 3) ? 6 : | ||
| 1457 | !strncmp (string+8, "Aug", 3) ? 7 : | ||
| 1458 | !strncmp (string+8, "Sep", 3) ? 8 : | ||
| 1459 | !strncmp (string+8, "Oct", 3) ? 9 : | ||
| 1460 | !strncmp (string+8, "Nov", 3) ? 10 : | ||
| 1461 | !strncmp (string+8, "Dec", 3) ? 11 : | ||
| 1462 | -1); | ||
| 1463 | tm.tm_year = ((1000 * (string[12]-'0') + | ||
| 1464 | 100 * (string[13]-'0') + | ||
| 1465 | 10 * (string[14]-'0') + | ||
| 1466 | (string[15]-'0')) | ||
| 1467 | - 1900); | ||
| 1468 | |||
| 1469 | tm.tm_isdst = 0; /* GMT is never in DST, right? */ | ||
| 1470 | |||
| 1471 | if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31) | ||
| 1472 | return 0; | ||
| 1473 | |||
| 1474 | /* | ||
| 1475 | This is actually wrong: we need to subtract the local timezone | ||
| 1476 | offset from GMT from this value. But, that's ok in this usage, | ||
| 1477 | because we only comparing these two GMT dates against each other, | ||
| 1478 | so it doesn't matter what time zone we parse them in. | ||
| 1479 | */ | ||
| 1480 | |||
| 1481 | t = mktime (&tm); | ||
| 1482 | if (t == (time_t) -1) t = 0; | ||
| 1483 | |||
| 1484 | if (verbose) { | ||
| 1485 | const char *s = string; | ||
| 1486 | while (*s && *s != '\r' && *s != '\n') | ||
| 1487 | fputc (*s++, stdout); | ||
| 1488 | printf (" ==> %lu\n", (unsigned long) t); | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | return t; | ||
| 1492 | |||
| 1493 | } else { | ||
| 1494 | return 0; | ||
| 1495 | } | ||
| 1496 | } | ||
| 1497 | |||
| 1498 | int | ||
| 1499 | check_document_dates (const curlhelp_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE]) | ||
| 1500 | { | ||
| 1501 | char *server_date = NULL; | ||
| 1502 | char *document_date = NULL; | ||
| 1503 | int date_result = STATE_OK; | ||
| 1504 | curlhelp_statusline status_line; | ||
| 1505 | struct phr_header headers[255]; | ||
| 1506 | size_t nof_headers = 255; | ||
| 1507 | size_t msglen; | ||
| 1508 | |||
| 1509 | int res = phr_parse_response (header_buf->buf, header_buf->buflen, | ||
| 1510 | &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen, | ||
| 1511 | headers, &nof_headers, 0); | ||
| 1512 | |||
| 1513 | server_date = get_header_value (headers, nof_headers, "date"); | ||
| 1514 | document_date = get_header_value (headers, nof_headers, "last-modified"); | ||
| 1515 | |||
| 1516 | if (!server_date || !*server_date) { | ||
| 1517 | snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg); | ||
| 1518 | date_result = max_state_alt(STATE_UNKNOWN, date_result); | ||
| 1519 | } else if (!document_date || !*document_date) { | ||
| 1520 | snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg); | ||
| 1521 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 1522 | } else { | ||
| 1523 | time_t srv_data = parse_time_string (server_date); | ||
| 1524 | time_t doc_data = parse_time_string (document_date); | ||
| 1525 | if (srv_data <= 0) { | ||
| 1526 | snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date); | ||
| 1527 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 1528 | } else if (doc_data <= 0) { | ||
| 1529 | snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date); | ||
| 1530 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 1531 | } else if (doc_data > srv_data + 30) { | ||
| 1532 | snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data); | ||
| 1533 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 1534 | } else if (doc_data < srv_data - maximum_age) { | ||
| 1535 | int n = (srv_data - doc_data); | ||
| 1536 | if (n > (60 * 60 * 24 * 2)) { | ||
| 1537 | snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24)); | ||
| 1538 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 1539 | } else { | ||
| 1540 | snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60); | ||
| 1541 | date_result = max_state_alt(STATE_CRITICAL, date_result); | ||
| 1542 | } | ||
| 1543 | } | ||
| 1544 | } | ||
| 1545 | |||
| 1546 | if (server_date) free (server_date); | ||
| 1547 | if (document_date) free (document_date); | ||
| 1548 | |||
| 1549 | return date_result; | ||
| 1550 | } | ||
