--- /dev/null
+From af01dd6fa3eb458e2fbb272703b0cae37ea54a9b Mon Sep 17 00:00:00 2001
+From: Marcin Jurkowski <marcin1j@gmail.com>
+Date: Tue, 11 Jul 2017 15:00:25 +0200
+Subject: [PATCH] uptime plugin: don't cache boot time and simplify Linux code
+
+Caching boottime on startup yields incorrect uptime values if system
+date changes after the daemon is started.
+This is almost certain to happen on embedded systems without RTC, where
+clock is set from NTP server at some point after boot process.
+
+On Linux, we can retrieve uptime directly by either reading /proc/uptime
+(it's sufficient to read a few bytes) or calling sysinfo() function.
+Use the latter since it's the most efficient way in speed, memory
+requirements and code simplicity terms.
+---
+ src/uptime.c | 71 ++++++++++++++++--------------------------------------------
+ 1 file changed, 19 insertions(+), 52 deletions(-)
+
+--- a/src/uptime.c
++++ b/src/uptime.c
+@@ -25,8 +25,7 @@
+ #include "plugin.h"
+
+ #if KERNEL_LINUX
+-#define STAT_FILE "/proc/stat"
+-/* Using /proc filesystem to retrieve the boot time, Linux only. */
++#include <sys/sysinfo.h>
+ /* #endif KERNEL_LINUX */
+
+ #elif HAVE_LIBKSTAT
+@@ -53,8 +52,6 @@
+ /*
+ * Global variables
+ */
+-/* boottime always used, no OS distinction */
+-static time_t boottime;
+
+ #if HAVE_LIBKSTAT
+ extern kstat_ctl_t *kc;
+@@ -72,8 +69,6 @@ static void uptime_submit(gauge_t value)
+ plugin_dispatch_values(&vl);
+ }
+
+-static int uptime_init(void) /* {{{ */
+-{
+ /*
+ * On most unix systems the uptime is calculated by looking at the boot
+ * time (stored in unix time, since epoch) and the current one. We are
+@@ -84,48 +79,21 @@ static int uptime_init(void) /* {{{ */
+ * the boot time, the plugin is unregistered and there is no chance to
+ * try again later. Nevertheless, this is very unlikely to happen.
+ */
+-
++static time_t uptime_get_sys(void) { /* {{{ */
++ time_t result;
+ #if KERNEL_LINUX
+- unsigned long starttime;
+- char buffer[1024];
+- int ret;
+- FILE *fh;
+-
+- ret = 0;
+-
+- fh = fopen(STAT_FILE, "r");
++ struct sysinfo info;
++ int status;
+
+- if (fh == NULL) {
++ status = sysinfo(&info);
++ if (status != 0) {
+ char errbuf[1024];
+- ERROR("uptime plugin: Cannot open " STAT_FILE ": %s",
++ ERROR("uptime plugin: Error calling sysinfo: %s",
+ sstrerror(errno, errbuf, sizeof(errbuf)));
+ return (-1);
+ }
+
+- while (fgets(buffer, 1024, fh) != NULL) {
+- /* look for the btime string and read the value */
+- ret = sscanf(buffer, "btime %lu", &starttime);
+- /* avoid further loops if btime has been found and read
+- * correctly (hopefully) */
+- if (ret == 1)
+- break;
+- }
+-
+- fclose(fh);
+-
+- /* loop done, check if no value has been found/read */
+- if (ret != 1) {
+- ERROR("uptime plugin: No value read from " STAT_FILE "");
+- return (-1);
+- }
+-
+- boottime = (time_t)starttime;
+-
+- if (boottime == 0) {
+- ERROR("uptime plugin: btime read from " STAT_FILE ", "
+- "but `boottime' is zero!");
+- return (-1);
+- }
++ result = (time_t)info.uptime;
+ /* #endif KERNEL_LINUX */
+
+ #elif HAVE_LIBKSTAT
+@@ -159,13 +127,13 @@ static int uptime_init(void) /* {{{ */
+ return (-1);
+ }
+
+- boottime = (time_t)knp->value.ui32;
+-
+- if (boottime == 0) {
++ if (knp->value.ui32 == 0) {
+ ERROR("uptime plugin: kstat_data_lookup returned success, "
+ "but `boottime' is zero!");
+ return (-1);
+ }
++
++ result = time(NULL) - (time_t)knp->value.ui32;
+ /* #endif HAVE_LIBKSTAT */
+
+ #elif HAVE_SYS_SYSCTL_H
+@@ -186,13 +154,13 @@ static int uptime_init(void) /* {{{ */
+ return (-1);
+ }
+
+- boottime = boottv.tv_sec;
+-
+- if (boottime == 0) {
++ if (boottv.tv_sec == 0) {
+ ERROR("uptime plugin: sysctl(3) returned success, "
+ "but `boottime' is zero!");
+ return (-1);
+ }
++
++ result = time(NULL) - boottv.tv_sec;
+ /* #endif HAVE_SYS_SYSCTL_H */
+
+ #elif HAVE_PERFSTAT
+@@ -212,18 +180,18 @@ static int uptime_init(void) /* {{{ */
+ if (hertz <= 0)
+ hertz = HZ;
+
+- boottime = time(NULL) - cputotal.lbolt / hertz;
++ result = cputotal.lbolt / hertz;
+ #endif /* HAVE_PERFSTAT */
+
+- return (0);
+-} /* }}} int uptime_init */
++ return result;
++} /* }}} int uptime_get_sys */
+
+ static int uptime_read(void) {
+ gauge_t uptime;
+ time_t elapsed;
+
+ /* calculate the amount of time elapsed since boot, AKA uptime */
+- elapsed = time(NULL) - boottime;
++ elapsed = uptime_get_sys();
+
+ uptime = (gauge_t)elapsed;
+
+@@ -233,6 +201,5 @@ static int uptime_read(void) {
+ }
+
+ void module_register(void) {
+- plugin_register_init("uptime", uptime_init);
+ plugin_register_read("uptime", uptime_read);
+ } /* void module_register */