summaryrefslogtreecommitdiffstats
path: root/plugins/check_curl.c
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2017-04-14 21:10:40 +0200
committerSven Nierlein <sven@nierlein.de>2018-10-22 16:30:31 +0200
commit9960c56e5e09caf33a8010e52cb32a931fb3ab2a (patch)
tree584c3efc2dc5b7f6870894ec7f4cac0662d51753 /plugins/check_curl.c
parentbbec77c7ecbc6ecaac06d2c8845f83a22546f273 (diff)
downloadmonitoring-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.c203
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";
37const char *copyright = "2006-2017"; 37const char *copyright = "2006-2017";
38const char *email = "devel@monitoring-plugins.org"; 38const 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;
142char *ca_cert = NULL; 146char *ca_cert = NULL;
143X509 *cert = NULL; 147X509 *cert = NULL;
144int no_body = FALSE; 148int no_body = FALSE;
149int maximum_age = -1;
145int address_family = AF_UNSPEC; 150int address_family = AF_UNSPEC;
146 151
147int process_arguments (int, char**); 152int process_arguments (int, char**);
@@ -156,6 +161,9 @@ void curlhelp_freebuffer (curlhelp_curlbuf*);
156int curlhelp_parse_statusline (const char*, curlhelp_statusline *); 161int curlhelp_parse_statusline (const char*, curlhelp_statusline *);
157void curlhelp_free_statusline (curlhelp_statusline *); 162void curlhelp_free_statusline (curlhelp_statusline *);
158char *perfd_time_ssl (double microsec); 163char *perfd_time_ssl (double microsec);
164char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header);
165static time_t parse_time_string (const char *string);
166int check_document_dates (const curlhelp_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]);
159 167
160void remove_newlines (char *); 168void remove_newlines (char *);
161void test_file (char *); 169void 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
1354char *perfd_time_ssl (double elapsed_time_ssl) 1390char *
1391perfd_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
1396char *
1397get_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
1407static time_t
1408parse_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
1498int
1499check_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}