[Git][NTPsec/ntpsec][master] 3 commits: stats, mode 6: Store two copies of protocol stats ...

James Browning gitlab at mg.gitlab.com
Sat Feb 13 16:59:15 UTC 2021



James Browning pushed to branch master at NTPsec / ntpsec


Commits:
e8b59ad4 by James Browning at 2021-02-13T08:48:20-08:00
stats, mode 6: Store two copies of protocol stats ...

The runtime total and as of the last hourly reset.
- - - - -
817bab48 by James Browning at 2021-02-13T08:48:20-08:00
pylib: add helpers for dual column statistics ...

 ntp.util.pretttyuptime()
 - prints uptime in '${days}D %H:%M:%S' or '%H:%M:%S'

ntpclients/ntpq:collectdisplay2()
- prints two column output badly
- - - - -
66ed781a by James Browning at 2021-02-13T08:48:20-08:00
ntpq: Display dual column protocol stats ...

This excludes uptime and control requests.
The control request should be part of control stats (unimplemented)
Uptime does not relate to when stats were reset.
- - - - -


6 changed files:

- include/ntpd.h
- ntpclients/ntpq.py
- ntpd/ntp_control.c
- ntpd/ntp_proto.c
- ntpd/ntp_util.c
- pylib/util.py


Changes:

=====================================
include/ntpd.h
=====================================
@@ -163,6 +163,29 @@ extern	int	sys_orphan;
 extern	double	sys_mindist;
 extern	double	sys_maxdisp;
 
+#define stat_sys_form(member)\
+extern uint64_t stat_##member(void);\
+extern uint64_t stat_total_##member(void)
+stat_sys_form(received);
+stat_sys_form(processed);
+stat_sys_form(restricted);
+stat_sys_form(newversion);
+stat_sys_form(oldversion);
+stat_sys_form(badlength);
+stat_sys_form(badauth);
+stat_sys_form(declined);
+stat_sys_form(limitrejected);
+stat_sys_form(kodsent);
+#undef stat_sys_form
+
+extern uptime_t stat_total_stattime(void);
+extern uptime_t stat_stattime(void);
+
+extern void increment_restricted(void);
+extern uptime_t stat_use_stattime(void);
+extern void set_use_stattime(uptime_t stattime);
+extern uptime_t	use_stattime;		/* time since usestats reset */
+
 extern	void	poll_update	(struct peer *, uint8_t);
 
 extern	void	clock_filter	(struct peer *, double, double, double);
@@ -173,23 +196,6 @@ extern	void	set_sys_tick_precision(double);
 extern	void	proto_config	(int, unsigned long, double);
 extern	void	proto_clr_stats (void);
 
-extern uptime_t stat_stattime(void);
-extern uint64_t stat_received(void);
-extern uint64_t stat_processed(void);
-extern uint64_t stat_restricted(void);
-extern void increment_restricted(void);
-extern uint64_t stat_newversion(void);
-extern uint64_t stat_oldversion(void);
-extern uint64_t stat_badlength(void);
-extern uint64_t stat_badauth(void);
-extern uint64_t stat_declined(void);
-extern uint64_t stat_limitrejected(void);
-extern uint64_t stat_kodsent(void);
-extern uptime_t stat_use_stattime(void);
-extern void set_use_stattime(uptime_t stattime);
-
-
-
 /* ntp_restrict.c */
 extern	void	init_restrict	(void);
 extern	unsigned short	restrictions	(sockaddr_u *);


=====================================
ntpclients/ntpq.py
=====================================
@@ -53,6 +53,7 @@ NTP_LFP = 0x7     # NTP timestamp
 NTP_MODE = 0x8    # peer mode
 NTP_2BIT = 0x9    # leap bits
 NTP_FLOAT = 0xa   # Float value
+NTP_UPTIME = 0xb  # uptime in days H:M:S (no frac)
 
 
 class Ntpq(cmd.Cmd):
@@ -393,6 +394,66 @@ function: set the primary receive time out
 usage: timeout [ msec ]
 """)
 
+    def collect_display2(self, variables):
+        "Query and display a collection of variables from the system."
+        try:
+            queried = self.session.readvar(0,
+                                           [v[0] for v in variables] +
+                                           [v[0] + '_r' for v in variables],
+                                           raw=True)
+        except ntp.packet.ControlException as e:
+            if ntp.control.CERR_UNKNOWNVAR == e.errorcode:
+                self.warn("Unknown variable.  Trying one at a time.")
+                varlist = [v[0] for v in variables] + \
+                          [v[0] + '_r' for v in variables]
+                items = []
+                for var in varlist:
+                    try:
+                        queried = self.session.readvar(0, [var],
+                                                       raw=True)
+                        for (name, (value, rawvalue)) in queried.items():
+                            items.append((name, (value, rawvalue)))
+                    except ntp.packet.ControlException as e:
+                        if ntp.control.CERR_UNKNOWNVAR == e.errorcode:
+                            items.append((var, "???"))
+                            continue
+                        raise e
+                queried = ntp.util.OrderedDict(items)
+            else:
+                self.warn(e.message)
+                return
+        except IOError as e:
+            self.warn(e.strerror)
+            return
+        if self.rawmode:
+            self.say(self.session.response)
+            return
+        try:
+            for (name, legend, fmt) in variables:
+                if name not in queried:
+                    continue
+                value = queried[name][0]
+                rawvalue = queried[name][1]
+                value2 = queried[name + '_r'][0]
+                rawvalue2 = queried[name + '_r'][1]
+                if fmt in (NTP_UINT, NTP_INT, NTP_FLOAT):
+                    if self.showunits:
+                        displayvalue = ntp.util.unitifyvar(rawvalue, name)
+                        displayvalue2 = ntp.util.unitifyvar(rawvalue2, name)
+                    else:
+                        displayvalue = value
+                        displayvalue2 = value2
+                    self.say("%13s \t%9d\t%9d\n" %
+                             (legend, displayvalue, displayvalue2))
+                elif fmt == NTP_UPTIME:
+                    self.say("%13s  %s\t%s\n" % (legend, ntp.util.prettyuptime(
+                        value), ntp.util.prettyuptime(value2)))
+                else:
+                    self.warn("unexpected vc type %s for %s, value %s"
+                              % (fmt, name, value, value2))
+        except KeyboardInterrupt:
+            self.warn("display interrupted")
+
     def collect_display(self, associd, variables, decodestatus):
         "Query and display a collection of variables from the system."
         try:
@@ -1463,11 +1524,13 @@ usage: kerninfo
         "display system uptime and packet counts"
         sysstats = (
             ("ss_uptime", "uptime:               ", NTP_INT),
-            ("ss_reset", "sysstats reset:       ", NTP_INT),
+            ("ss_numctlreq", "control requests:     ", NTP_INT),
+        )
+        sysstats2 = (
+            ("ss_reset", "sysstats reset:       ", NTP_UPTIME),
             ("ss_received", "packets received:     ", NTP_INT),
             ("ss_thisver", "current version:      ", NTP_INT),
             ("ss_oldver", "older version:        ", NTP_INT),
-            ("ss_numctlreq", "control requests:     ", NTP_INT),
             ("ss_badformat", "bad length or format: ", NTP_INT),
             ("ss_badauth", "authentication failed:", NTP_INT),
             ("ss_declined", "declined:             ", NTP_INT),
@@ -1477,6 +1540,7 @@ usage: kerninfo
             ("ss_processed", "processed for time:   ", NTP_INT),
         )
         self.collect_display(associd=0, variables=sysstats, decodestatus=False)
+        self.collect_display2(variables=sysstats2)
 
     def help_sysstats(self):
         self.say("""\


=====================================
ntpd/ntp_control.c
=====================================
@@ -348,45 +348,63 @@ static const struct ctl_var sys_var[] = {
 	{ CS_AUTHCMACFAIL,	RO, "authcmacfails" },
 #define CS_K_LOCKCLOCK		105
 	{ CS_K_LOCKCLOCK,	RO, "lockclock" },
-
+#define CS_MRU_HASHSLOTS	106
+	{ CS_MRU_HASHSLOTS,		RO, "mru_hashslots" },
+#define	CS_SS_RESET_R		107
+	{ CS_SS_RESET_R,		RO, "ss_reset_r" },
+#define	CS_SS_RECEIVED_R		108
+	{ CS_SS_RECEIVED_R,	RO, "ss_received_r" },
+#define	CS_SS_THISVER_R		109
+	{ CS_SS_THISVER_R,	RO, "ss_thisver_r" },
+#define	CS_SS_OLDVER_R		110
+	{ CS_SS_OLDVER_R,		RO, "ss_oldver_r" },
+#define	CS_SS_BADFORMAT_R		111
+	{ CS_SS_BADFORMAT_R,	RO, "ss_badformat_r" },
+#define	CS_SS_BADAUTH_R		112
+	{ CS_SS_BADAUTH_R,	RO, "ss_badauth_r" },
+#define	CS_SS_DECLINED_R		113
+	{ CS_SS_DECLINED_R,	RO, "ss_declined_r" },
+#define	CS_SS_RESTRICTED_R	114
+	{ CS_SS_RESTRICTED_R,	RO, "ss_restricted_r" },
+#define	CS_SS_LIMITED_R		115
+	{ CS_SS_LIMITED_R,	RO, "ss_limited_r" },
+#define	CS_SS_KODSENT_R		116
+	{ CS_SS_KODSENT_R,	RO, "ss_kodsent_r" },
+#define	CS_SS_PROCESSED_R		117
+	{ CS_SS_PROCESSED_R,	RO, "ss_processed_r" },
 #ifndef DISABLE_NTS
-#define CS_nts_client_send	106
+#define CS_nts_client_send	118
 	{ CS_nts_client_send,		RO, "nts_client_send" },
-#define CS_nts_client_recv_good	107
+#define CS_nts_client_recv_good	119
 	{ CS_nts_client_recv_good,	RO, "nts_client_recv_good" },
-#define CS_nts_client_recv_bad	108
+#define CS_nts_client_recv_bad	120
 	{ CS_nts_client_recv_bad,	RO, "nts_client_recv_bad" },
-#define CS_nts_server_send	109
+#define CS_nts_server_send	121
 	{ CS_nts_server_send,		RO, "nts_server_send" },
-#define CS_nts_server_recv_good	110
+#define CS_nts_server_recv_good	122
 	{ CS_nts_server_recv_good,	RO, "nts_server_recv_good" },
-#define CS_nts_server_recv_bad	111
+#define CS_nts_server_recv_bad	123
 	{ CS_nts_server_recv_bad,	RO, "nts_server_recv_bad" },
 
-#define CS_nts_cookie_make		112
+#define CS_nts_cookie_make		124
 	{ CS_nts_cookie_make,		RO, "nts_cookie_make" },
-#define CS_nts_cookie_decode		113
+#define CS_nts_cookie_decode		125
 	{ CS_nts_cookie_decode,		RO, "nts_cookie_decode" },
-#define CS_nts_cookie_decode_old	114
+#define CS_nts_cookie_decode_old	126
 	{ CS_nts_cookie_decode_old,	RO, "nts_cookie_decode_old" },
-#define CS_nts_cookie_decode_too_old	115
+#define CS_nts_cookie_decode_too_old	127
 	{ CS_nts_cookie_decode_too_old,	RO, "nts_cookie_decode_too_old" },
-#define CS_nts_cookie_decode_error	116
+#define CS_nts_cookie_decode_error	128
 	{ CS_nts_cookie_decode_error,	RO, "nts_cookie_decode_error" },
 
-#define CS_nts_ke_serves_good	117
+#define CS_nts_ke_serves_good	129
 	{ CS_nts_ke_serves_good,	RO, "nts_ke_serves_good" },
-#define CS_nts_ke_serves_bad	118
+#define CS_nts_ke_serves_bad	130
 	{ CS_nts_ke_serves_bad,		RO, "nts_ke_serves_bad" },
-#define CS_nts_ke_probes_good	119
+#define CS_nts_ke_probes_good	131
 	{ CS_nts_ke_probes_good,	RO, "nts_ke_probes_good" },
-#define CS_nts_ke_probes_bad	120
+#define CS_nts_ke_probes_bad	132
 	{ CS_nts_ke_probes_bad,		RO, "nts_ke_probes_bad" },
-#define CS_MRU_HASHSLOTS	121
-	{ CS_MRU_HASHSLOTS,		RO, "mru_hashslots" },
-#else
-#define CS_MRU_HASHSLOTS	106
-	{ CS_MRU_HASHSLOTS,		RO, "mru_hashslots" },
 #endif
 #define	CS_MAXCODE		((sizeof(sys_var)/sizeof(sys_var[0])) - 1)
 	{ 0,                    EOV, "" }
@@ -1547,7 +1565,7 @@ ctl_putsys(
 
 	CASE_UINT(CS_SS_UPTIME, current_time);
 
-	CASE_UINT(CS_SS_RESET, current_time - stat_stattime());
+	CASE_UINT(CS_SS_RESET, stat_stattime());
 
 	CASE_UINT(CS_SS_RECEIVED, stat_received());
 
@@ -1569,6 +1587,50 @@ ctl_putsys(
 
 	CASE_UINT(CS_SS_PROCESSED, stat_processed());
 
+	case CS_SS_RESET_R:
+		ctl_putuint(sys_var[varid].text, stat_total_stattime());
+		break;
+
+	case CS_SS_RECEIVED_R:
+        ctl_putuint(sys_var[varid].text, stat_total_received());
+		break;
+
+	case CS_SS_THISVER_R:
+		ctl_putuint(sys_var[varid].text, stat_total_newversion());
+		break;
+
+	case CS_SS_OLDVER_R:
+		ctl_putuint(sys_var[varid].text, stat_total_oldversion());
+		break;
+
+	case CS_SS_BADFORMAT_R:
+		ctl_putuint(sys_var[varid].text, stat_total_badlength());
+		break;
+
+	case CS_SS_BADAUTH_R:
+		ctl_putuint(sys_var[varid].text, stat_total_badauth());
+		break;
+
+	case CS_SS_DECLINED_R:
+		ctl_putuint(sys_var[varid].text, stat_total_declined());
+		break;
+
+	case CS_SS_RESTRICTED_R:
+		ctl_putuint(sys_var[varid].text, stat_total_restricted());
+		break;
+
+	case CS_SS_LIMITED_R:
+		ctl_putuint(sys_var[varid].text, stat_total_limitrejected());
+		break;
+
+	case CS_SS_KODSENT_R:
+		ctl_putuint(sys_var[varid].text, stat_total_kodsent());
+		break;
+
+	case CS_SS_PROCESSED_R:
+		ctl_putuint(sys_var[varid].text, stat_total_processed());
+		break;
+
 	case CS_AUTHDELAY:
 		dtemp = lfptod(sys_authdelay);
 		ctl_putdbl(sys_var[varid].text, dtemp * MS_PER_S);


=====================================
ntpd/ntp_proto.c
=====================================
@@ -137,10 +137,7 @@ int	sys_maxclock = NTP_MAXCLOCK; /* maximum candidates */
 int	sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */
 static int sys_orphwait = NTP_ORPHWAIT; /* orphan wait */
 
-/*
- * Statistics counters - first the good, then the bad
- * These get reset every hour if sysstats is enabled.
- */
+// proto stats structure and variables
 struct statistics_counters {
 	uptime_t	sys_stattime;		/* time since sysstats reset */
 	uint64_t	sys_received;		/* packets received */
@@ -153,77 +150,54 @@ struct statistics_counters {
 	uint64_t	sys_declined;		/* declined */
 	uint64_t	sys_limitrejected;	/* rate exceeded */
 	uint64_t	sys_kodsent;		/* KoD sent */
-	uptime_t	use_stattime;		/* time since usestats reset */
 };
-volatile struct statistics_counters stat_count;
-
-uptime_t stat_stattime(void)
-{
-  return stat_count.sys_stattime;
-}
-
-uint64_t stat_received(void)
-{
-  return stat_count.sys_received;
-}
-
-uint64_t stat_processed(void)
-{
-  return stat_count.sys_processed;
-}
-
-uint64_t stat_restricted(void)
-{
-  return stat_count.sys_restricted;
-}
+volatile struct statistics_counters stat_proto_hourago, stat_proto_total;
 
-void increment_restricted(void)
-{
-  stat_count.sys_restricted++;
-}
-
-uint64_t stat_newversion(void)
-{
-  return stat_count.sys_newversion;
-}
+uptime_t	use_stattime;		/* time since usestats reset */
 
-uint64_t stat_oldversion(void)
+uptime_t stat_stattime(void)
 {
-  return stat_count.sys_oldversion;
+  return current_time - stat_proto_hourago.sys_stattime;
 }
 
-uint64_t stat_badlength(void)
+uptime_t stat_total_stattime(void)
 {
-  return stat_count.sys_badlength;
+  return current_time - stat_proto_total.sys_stattime;
 }
 
-uint64_t stat_badauth(void)
-{
-  return stat_count.sys_badauth;
+#define stat_sys_dumps(member)\
+uint64_t stat_##member(void) {\
+  return stat_proto_total.sys_##member - stat_proto_hourago.sys_##member;\
+}\
+uint64_t stat_total_##member(void) {\
+  return stat_proto_total.sys_##member;\
 }
 
-uint64_t stat_declined(void)
-{
-  return stat_count.sys_declined;
-}
+stat_sys_dumps(received)
+stat_sys_dumps(processed)
+stat_sys_dumps(restricted)
+stat_sys_dumps(newversion)
+stat_sys_dumps(oldversion)
+stat_sys_dumps(badlength)
+stat_sys_dumps(badauth)
+stat_sys_dumps(declined)
+stat_sys_dumps(limitrejected)
+stat_sys_dumps(kodsent)
 
-uint64_t stat_limitrejected(void)
-{
-  return stat_count.sys_limitrejected;
-}
+#undef stat_sys_dumps
 
-uint64_t stat_kodsent(void)
+void increment_restricted(void)
 {
-  return stat_count.sys_kodsent;
+  stat_proto_total.sys_restricted++;
 }
 
 uptime_t stat_use_stattime(void)
 {
-  return stat_count.use_stattime;
+  return current_time - use_stattime;
 }
 
 void set_use_stattime(uptime_t stattime) {
-  stat_count.use_stattime = stattime;
+  use_stattime = stattime;
 }
 
 
@@ -520,7 +494,7 @@ handle_procpkt(
 		}
 	} else {
 		/* This case should be unreachable. */
-		stat_count.sys_declined++;
+		stat_proto_total.sys_declined++;
 		return;
 	}
 
@@ -666,10 +640,10 @@ receive(
 	auth_info* auth = NULL;  /* !NULL if authenticated */
 	int mode;
 
-	stat_count.sys_received++;
+	stat_proto_total.sys_received++;
 
 	if(!is_packet_not_low_rot(rbufp)) {
-		stat_count.sys_badlength++;
+		stat_proto_total.sys_badlength++;
 		return;
 	}
 
@@ -678,19 +652,19 @@ receive(
 	restrict_mask = restrictions(&rbufp->recv_srcadr);
 
 	if(check_early_restrictions(rbufp, restrict_mask)) {
-		stat_count.sys_restricted++;
+		stat_proto_total.sys_restricted++;
 		return;
 	}
 
 	restrict_mask = ntp_monitor(rbufp, restrict_mask);
 	if (restrict_mask & RES_LIMITED) {
-		stat_count.sys_limitrejected++;
+		stat_proto_total.sys_limitrejected++;
 		if(!(restrict_mask & RES_KOD)) { return; }
 	}
 
 	if(is_control_packet(rbufp)) {
 		process_control(rbufp, restrict_mask);
-		stat_count.sys_processed++;
+		stat_proto_total.sys_processed++;
 		return;
 	}
 
@@ -701,18 +675,18 @@ receive(
 	{
 	uint8_t hisversion = PKT_VERSION(rbufp->recv_buffer[0]);
 	if (hisversion == NTP_VERSION) {
-		stat_count.sys_newversion++;		/* new version */
+		stat_proto_total.sys_newversion++;		/* new version */
 	} else if (!(restrict_mask & RES_VERSION) && hisversion >=
 	    NTP_OLDVERSION) {
-		stat_count.sys_oldversion++;		/* previous version */
+		stat_proto_total.sys_oldversion++;		/* previous version */
 	} else {
-		stat_count.sys_badlength++;
+		stat_proto_total.sys_badlength++;
 		return;			/* old version */
 	}
 	}
 
 	if (!parse_packet(rbufp)) {
-		stat_count.sys_badlength++;
+		stat_proto_total.sys_badlength++;
 		return;
 	}
 
@@ -724,7 +698,7 @@ receive(
 	     * with a different key. */
 	    peer = findpeer(rbufp);
 	    if (NULL == peer) {
-		stat_count.sys_declined++;
+		stat_proto_total.sys_declined++;
 		return;
 	    }
 	}
@@ -757,7 +731,7 @@ receive(
 				 (int)(rbufp->recv_length - (rbufp->mac_len + 4)),
 				 (int)(rbufp->mac_len + 4))) {
 
-			stat_count.sys_badauth++;
+			stat_proto_total.sys_badauth++;
 			if(peer != NULL) {
 				peer->badauth++;
 				peer->cfg.flags &= ~FLAG_AUTHENTIC;
@@ -775,12 +749,12 @@ receive(
 			  rbufp->recv_buffer, rbufp->recv_length)
 #endif
 ) {
-			stat_count.sys_declined++;
+			stat_proto_total.sys_declined++;
 			maybe_log_junk("EX-REQ", rbufp);
 			break;
 		}
 		fast_xmit(rbufp, auth, restrict_mask);
-		stat_count.sys_processed++;
+		stat_proto_total.sys_processed++;
 		break;
 	    case MODE_SERVER:  /* Reply to our request to a server. */
 		if ((peer->cfg.flags & FLAG_NTS)
@@ -790,7 +764,7 @@ receive(
 		          rbufp->recv_buffer, rbufp->recv_length)
 #endif
 )) {
-		    stat_count.sys_declined++;
+		    stat_proto_total.sys_declined++;
 		    maybe_log_junk("EX-REP", rbufp);
 		    break;
 		}
@@ -798,7 +772,7 @@ receive(
 		peer->cfg.flags |= FLAG_AUTHENTIC;
 		peer->timereceived = current_time;
 		handle_procpkt(rbufp, peer);
-		stat_count.sys_processed++;
+		stat_proto_total.sys_processed++;
 		peer->processed++;
 		break;
 	    default:
@@ -806,7 +780,7 @@ receive(
 		   which are a security nightmare.  So they go to the
 		   bit bucket until this improves.
 		*/
-		stat_count.sys_declined++;
+		stat_proto_total.sys_declined++;
 		break;
 	}
 
@@ -2236,7 +2210,7 @@ fast_xmit(
 	 * synchronization.
 	 */
 	if (flags & RES_KOD) {
-		stat_count.sys_kodsent++;
+		stat_proto_total.sys_kodsent++;
 		xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
 		    PKT_VERSION(rbufp->pkt.li_vn_mode), MODE_SERVER);
 		xpkt.stratum = STRATUM_PKT_UNSPEC;
@@ -2807,10 +2781,10 @@ init_proto(const bool verbose)
 #endif
 	get_systime(&dummy);
 	sys_survivors = 0;
-	stat_count.sys_stattime = current_time;
+	stat_proto_total.sys_stattime = current_time;
 	orphwait = current_time + (unsigned long)sys_orphwait;
 	proto_clr_stats();
-	stat_count.use_stattime = current_time;
+	use_stattime = current_time;
 	clock_ctl.hardpps_enable = false;
 	stats_control = true;
 }
@@ -2929,17 +2903,8 @@ proto_config(
 void
 proto_clr_stats(void)
 {
-	stat_count.sys_stattime = current_time;
-	stat_count.sys_received = 0;
-	stat_count.sys_processed = 0;
-	stat_count.sys_newversion = 0;
-	stat_count.sys_oldversion = 0;
-	stat_count.sys_declined = 0;
-	stat_count.sys_restricted = 0;
-	stat_count.sys_badlength = 0;
-	stat_count.sys_badauth = 0;
-	stat_count.sys_limitrejected = 0;
-	stat_count.sys_kodsent = 0;
+    stat_proto_hourago = stat_proto_total;
+	stat_proto_hourago.sys_stattime = current_time;
 }
 
 


=====================================
ntpd/ntp_util.c
=====================================
@@ -10,7 +10,6 @@
 #include "ntp_filegen.h"
 #include "ntp_leapsec.h"
 #include "ntp_stdlib.h"
-#include "ntp_stdlib.h"
 #include "ntp_auth.h"
 #include "ntpd.h"
 #include "timespecops.h"


=====================================
pylib/util.py
=====================================
@@ -1434,4 +1434,12 @@ except ImportError:  # pragma: no cover
             for key in self.__keys:
                 yield key
 
+
+def prettyuptime(uptime):
+    result = ''
+    if uptime >= 86400:
+        result += '%dD ' % (uptime // 86400)
+    result += '%02d:%02d:%02d' % ((uptime % 86400) //
+                                  3600, (uptime % 3600) // 60, uptime % 60)
+    return result
 # end



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/fae7938abd5cee75dcde482bb46a5bf82ff1563c...66ed781a9e5d2df2664368ecb9bf193eef24d803

-- 
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/fae7938abd5cee75dcde482bb46a5bf82ff1563c...66ed781a9e5d2df2664368ecb9bf193eef24d803
You're receiving this email because of your account on gitlab.com.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20210213/0e34344b/attachment-0001.htm>


More information about the vc mailing list