[monitoring-plugins] check_ntp_time: implement modern output

Lorenz Kästle git at monitoring-plugins.org
Wed Nov 5 11:00:16 CET 2025


 Module: monitoring-plugins
 Branch: master
 Commit: b35853ee4e10c4485a9521d77c95aecae6573e64
 Author: Lorenz Kästle <12514511+RincewindsHat at users.noreply.github.com>
   Date: Tue Nov  4 12:08:59 2025 +0100
    URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=b35853ee

check_ntp_time: implement modern output

---

 plugins/check_ntp_time.c          | 160 +++++++++++++++++++-------------------
 plugins/check_ntp_time.d/config.h |  13 +++-
 2 files changed, 93 insertions(+), 80 deletions(-)

diff --git a/plugins/check_ntp_time.c b/plugins/check_ntp_time.c
index ad69b804..eee9c298 100644
--- a/plugins/check_ntp_time.c
+++ b/plugins/check_ntp_time.c
@@ -34,12 +34,10 @@
  *
  *****************************************************************************/
 
-const char *progname = "check_ntp_time";
-const char *copyright = "2006-2024";
-const char *email = "devel at monitoring-plugins.org";
-
+#include "output.h"
 #include "common.h"
 #include "netutils.h"
+#include "perfdata.h"
 #include "utils.h"
 #include "states.h"
 #include "thresholds.h"
@@ -47,6 +45,10 @@ const char *email = "devel at monitoring-plugins.org";
 
 static int verbose = 0;
 
+const char *progname = "check_ntp_time";
+const char *copyright = "2006-2024";
+const char *email = "devel at monitoring-plugins.org";
+
 typedef struct {
 	int errorcode;
 	check_ntp_time_config config;
@@ -61,9 +63,6 @@ void print_usage(void);
 #	define AVG_NUM 4
 #endif
 
-/* max size of control message data */
-#define MAX_CM_SIZE 468
-
 /* this structure holds everything in an ntp request/response as per rfc1305 */
 typedef struct {
 	uint8_t flags;    /* byte with leapindicator,vers,mode. see macros */
@@ -169,7 +168,9 @@ typedef struct {
 						   : 0)
 
 /* convert a struct timeval to a double */
-#define TVasDOUBLE(x) (double)(x.tv_sec + (0.000001 * x.tv_usec))
+static double TVasDOUBLE(struct timeval time) {
+	return ((double)time.tv_sec + (0.000001 * (double)time.tv_usec));
+}
 
 /* convert an ntp 64-bit fp number to a struct timeval */
 #define NTP64toTV(n, t)                                                                            \
@@ -262,8 +263,8 @@ void setup_request(ntp_message *message) {
 /* 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 best_server = -1;
+static int best_offset_server(const ntp_server_results *slist, int nservers) {
+	int best_server_index = -1;
 
 	/* for each server */
 	for (int cserver = 0; cserver < nservers; cserver++) {
@@ -286,33 +287,33 @@ int best_offset_server(const ntp_server_results *slist, int nservers) {
 		}
 
 		/* If we don't have a server yet, use the first one */
-		if (best_server == -1) {
-			best_server = cserver;
-			DBG(printf("using peer %d as our first candidate\n", best_server));
+		if (best_server_index == -1) {
+			best_server_index = cserver;
+			DBG(printf("using peer %d as our first candidate\n", best_server_index));
 			continue;
 		}
 
 		/* compare the server to the best one we've seen so far */
 		/* does it have an equal or better stratum? */
-		DBG(printf("comparing peer %d with peer %d\n", cserver, best_server));
-		if (slist[cserver].stratum <= slist[best_server].stratum) {
-			DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server));
+		DBG(printf("comparing peer %d with peer %d\n", cserver, best_server_index));
+		if (slist[cserver].stratum <= slist[best_server_index].stratum) {
+			DBG(printf("stratum for peer %d <= peer %d\n", cserver, best_server_index));
 			/* does it have an equal or better dispersion? */
-			if (slist[cserver].rtdisp <= slist[best_server].rtdisp) {
-				DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server));
+			if (slist[cserver].rtdisp <= slist[best_server_index].rtdisp) {
+				DBG(printf("dispersion for peer %d <= peer %d\n", cserver, best_server_index));
 				/* does it have a better rtdelay? */
-				if (slist[cserver].rtdelay < slist[best_server].rtdelay) {
-					DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server));
-					best_server = cserver;
-					DBG(printf("peer %d is now our best candidate\n", best_server));
+				if (slist[cserver].rtdelay < slist[best_server_index].rtdelay) {
+					DBG(printf("rtdelay for peer %d < peer %d\n", cserver, best_server_index));
+					best_server_index = cserver;
+					DBG(printf("peer %d is now our best candidate\n", best_server_index));
 				}
 			}
 		}
 	}
 
-	if (best_server >= 0) {
-		DBG(printf("best server selected: peer %d\n", best_server));
-		return best_server;
+	if (best_server_index >= 0) {
+		DBG(printf("best server selected: peer %d\n", best_server_index));
+		return best_server_index;
 	}
 	DBG(printf("no peers meeting synchronization criteria :(\n"));
 	return -1;
@@ -323,7 +324,11 @@ 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, const char *port, mp_state_enum *status, int time_offset) {
+typedef struct {
+	mp_state_enum offset_result;
+	double offset;
+} offset_request_wrapper;
+static offset_request_wrapper offset_request(const char *host, const char *port, int time_offset) {
 	/* setup hints to only return results from getaddrinfo that we'd like */
 	struct addrinfo hints;
 	memset(&hints, 0, sizeof(struct addrinfo));
@@ -462,11 +467,16 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
 		die(STATE_CRITICAL, "NTP CRITICAL: No response from NTP server\n");
 	}
 
+	offset_request_wrapper result = {
+		.offset = 0,
+		.offset_result = STATE_UNKNOWN,
+	};
+
 	/* now, pick the best server from the list */
 	double avg_offset = 0.;
 	int best_index = best_offset_server(servers, num_hosts);
 	if (best_index < 0) {
-		*status = STATE_UNKNOWN;
+		result.offset_result = STATE_UNKNOWN;
 	} else {
 		/* finally, calculate the average offset */
 		for (int i = 0; i < servers[best_index].num_responses; i++) {
@@ -488,10 +498,12 @@ double offset_request(const char *host, const char *port, mp_state_enum *status,
 	if (verbose) {
 		printf("overall average offset: %.10g\n", avg_offset);
 	}
-	return avg_offset;
+
+	result.offset = avg_offset;
+	return result;
 }
 
-check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
+static check_ntp_time_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'},
@@ -515,9 +527,6 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
 		.config = check_ntp_time_config_init(),
 	};
 
-	char *owarn = "60";
-	char *ocrit = "120";
-
 	while (true) {
 		int option = 0;
 		int option_char = getopt_long(argc, argv, "Vhv46qw:c:t:H:p:o:", longopts, &option);
@@ -540,12 +549,24 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
 		case 'q':
 			result.config.quiet = true;
 			break;
-		case 'w':
-			owarn = optarg;
-			break;
-		case 'c':
-			ocrit = optarg;
-			break;
+		case 'w': {
+			mp_range_parsed tmp = mp_parse_range_string(optarg);
+			if (tmp.error != MP_PARSING_SUCCES) {
+				die(STATE_UNKNOWN, "failed to parse warning threshold");
+			}
+
+			result.config.offset_thresholds =
+				mp_thresholds_set_warn(result.config.offset_thresholds, tmp.range);
+		} break;
+		case 'c': {
+			mp_range_parsed tmp = mp_parse_range_string(optarg);
+			if (tmp.error != MP_PARSING_SUCCES) {
+				die(STATE_UNKNOWN, "failed to parse crit threshold");
+			}
+
+			result.config.offset_thresholds =
+				mp_thresholds_set_crit(result.config.offset_thresholds, tmp.range);
+		} break;
 		case 'H':
 			if (!is_host(optarg)) {
 				usage2(_("Invalid hostname/address"), optarg);
@@ -582,16 +603,9 @@ check_ntp_time_config_wrapper process_arguments(int argc, char **argv) {
 		usage4(_("Hostname was not supplied"));
 	}
 
-	set_thresholds(&result.config.offset_thresholds, owarn, ocrit);
-
 	return result;
 }
 
-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);
-}
-
 int main(int argc, char *argv[]) {
 	setlocale(LC_ALL, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
@@ -614,46 +628,36 @@ int main(int argc, char *argv[]) {
 	/* set socket timeout */
 	alarm(socket_timeout);
 
-	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);
-	if (offset_result == STATE_UNKNOWN) {
-		result = ((!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
-	} else {
-		result = get_status(fabs(offset), config.offset_thresholds);
-	}
+	mp_check overall = mp_check_init();
 
-	char *result_line;
-	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;
-	}
+	mp_subcheck sc_offset = mp_subcheck_init();
+	offset_request_wrapper offset_result =
+		offset_request(config.server_address, config.port, config.time_offset);
 
-	char *perfdata_line;
-	if (offset_result == STATE_UNKNOWN) {
-		xasprintf(&result_line, "%s %s", result_line, _("Offset unknown"));
-		xasprintf(&perfdata_line, "");
-	} else {
-		xasprintf(&result_line, "%s %s %.10g secs", result_line, _("Offset"), offset);
-		xasprintf(&perfdata_line, "%s", perfd_offset(offset, config.offset_thresholds));
+	if (offset_result.offset_result == STATE_UNKNOWN) {
+		sc_offset =
+			mp_set_subcheck_state(sc_offset, (!config.quiet) ? STATE_UNKNOWN : STATE_CRITICAL);
+		xasprintf(&sc_offset.output, "Offset unknown");
+		mp_add_subcheck_to_check(&overall, sc_offset);
+		mp_exit(overall);
 	}
-	printf("%s|%s\n", result_line, perfdata_line);
+
+	xasprintf(&sc_offset.output, "Offset: %.10g s", offset_result.offset);
+
+	mp_perfdata pd_offset = perfdata_init();
+	pd_offset = mp_set_pd_value(pd_offset, fabs(offset_result.offset));
+	pd_offset.label = "offset";
+	pd_offset = mp_pd_set_thresholds(pd_offset, config.offset_thresholds);
+
+	sc_offset = mp_set_subcheck_state(sc_offset, mp_get_pd_status(pd_offset));
+
+	mp_add_perfdata_to_subcheck(&sc_offset, pd_offset);
+	mp_add_subcheck_to_check(&overall, sc_offset);
 
 	if (config.server_address != NULL) {
 		free(config.server_address);
 	}
-	exit(result);
+	mp_exit(overall);
 }
 
 void print_help(void) {
diff --git a/plugins/check_ntp_time.d/config.h b/plugins/check_ntp_time.d/config.h
index 99dabbbd..a62e4ceb 100644
--- a/plugins/check_ntp_time.d/config.h
+++ b/plugins/check_ntp_time.d/config.h
@@ -11,7 +11,7 @@ typedef struct {
 	bool quiet;
 	int time_offset;
 
-	thresholds *offset_thresholds;
+	mp_thresholds offset_thresholds;
 } check_ntp_time_config;
 
 check_ntp_time_config check_ntp_time_config_init() {
@@ -22,7 +22,16 @@ check_ntp_time_config check_ntp_time_config_init() {
 		.quiet = false,
 		.time_offset = 0,
 
-		.offset_thresholds = NULL,
+		.offset_thresholds = mp_thresholds_init(),
 	};
+
+	mp_range warning = mp_range_init();
+	warning = mp_range_set_end(warning, mp_create_pd_value(60));
+	tmp.offset_thresholds = mp_thresholds_set_warn(tmp.offset_thresholds, warning);
+
+	mp_range critical = mp_range_init();
+	critical = mp_range_set_end(warning, mp_create_pd_value(120));
+	tmp.offset_thresholds = mp_thresholds_set_crit(tmp.offset_thresholds, critical);
+
 	return tmp;
 }



More information about the Commits mailing list