uptime plugin: Add a plugin to measure the time a system has been running.
authorMarco Chiappero <marco@absence.it>
Thu, 19 Mar 2009 12:30:32 +0000 (13:30 +0100)
committerFlorian Forster <octo@huhu.verplant.org>
Thu, 19 Mar 2009 12:30:32 +0000 (13:30 +0100)
Florian Forster ha scritto:
>Hi Marco,

Hi Florian,

> Checking the KSTAT_TYPE_NAMED twice (three times if you count the
> assertion) is of course not necessary. I suspect a copy'n'paste error ;)

Me too. Please remove the second one at least, kstat_read do not change
ks_type.

>A quick `grep' revealed that `get_kstat' is only used in two places, the
>memory and the swap plugin - both trying to figure out the pagesize. If
>you need something else than `KSTAT_TYPE_NAMED', I'd suggest to move the
>checks to a new function `get_kstat_pagesize' and use that function in
>the two plugins.

I went for the kstat.h only solution.
Ok, I'm attaching the code, please check the preprocessor directives for
the includes section, I don't much about defines created by the build
system and its names.
I'm using it right now on a linux machine, but I haven't tested yet on
Solaris and BSDs. Today I tried to compile it on OpenSolaris, but after
fighting with the packaging system and many other things I gave up and
decided I won't try anymore. So, since I'd like to say the plugin is
done after testing it, Solaris testers are wanted :P
As soon as I can I will edit the wiki page, but first I want to have a
closer look to the iptable plugin and see how many modifications are
needed for IPv6 and then have an opinion about the single vs. double
plugin approach.
See you soon :)

Regars,
Marco

src/types.db
src/uptime.c [new file with mode: 0644]

index f0525fe..05f5f79 100644 (file)
@@ -110,6 +110,7 @@ temperature         value:GAUGE:-273.15:U
 time_dispersion                seconds:GAUGE:-1000000:1000000
 timeleft               timeleft:GAUGE:0:3600
 time_offset            seconds:GAUGE:-1000000:1000000
+uptime                 value:GAUGE:0:4294967295
 users                  users:GAUGE:0:65535
 virt_cpu_total         ns:COUNTER:0:256000000000
 virt_vcpu              ns:COUNTER:0:1000000000
diff --git a/src/uptime.c b/src/uptime.c
new file mode 100644 (file)
index 0000000..f98be06
--- /dev/null
@@ -0,0 +1,227 @@
+/**
+ * collectd - src/uptime.c
+ * Copyright (C) 2009  Marco Chiappero
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Marco Chiappero <marco at absence.it>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if KERNEL_LINUX
+# define UPTIME_FILE "/proc/uptime"
+/*
+   No need for includes, using /proc filesystem, Linux only.
+*/
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
+/* something to include? maybe <sys/sysinfo.h> ? */
+/*
+   Using kstats chain to retrieve the boot time, this applies to:
+       - Solaris / OpenSolaris
+*/
+/* #endif HAVE_LIBKSTAT */
+
+#elif HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+/*
+   Using sysctl interface to retrieve the boot time, this applies to:
+       - *BSD
+        - Darwin / OS X
+*/
+/* #endif HAVE_SYS_SYSCTL_H */
+
+#else
+# error "No applicable input method."
+#endif
+
+
+#if !KERNEL_LINUX
+#define INIT_FAILED "uptime plugin: unable to calculate uptime, the plugin will be disabled."
+#endif
+
+
+#if KERNEL_LINUX
+/* global variables not needed*/
+
+#elif HAVE_SYS_SYSCTL_H || HAVE_LIBKSTAT
+static time_t boottime;
+# if HAVE_LIBKSTAT
+extern kstat_ctl_t *kc;
+# endif
+
+#endif
+
+
+static void uptime_submit (gauge_t uptime)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = uptime;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "uptime", sizeof (vl.plugin));
+       sstrncpy (vl.type, "uptime", sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+}
+
+#if !KERNEL_LINUX
+static int uptime_init (void)
+{
+/*     NOTE
+
+       On unix systems other than Linux there is no /proc filesystem which
+       calculates the uptime every time we call a read for the /proc/uptime
+       file, the only information available is the boot time (in unix time,
+       since epoch). Hence there is no need to read, every time the
+       plugin_read is called, a value that won't change: this is a right
+       task for the uptime_init function. However, since uptime_init is run
+       only once, if the function fails in retrieving the boot time, the
+       plugin is unregistered and there is no chance to try again later.
+       Nevertheless, this is very unlikely to happen.
+
+*/
+
+# if HAVE_LIBKSTAT
+
+       kstat_t *ksp;
+       kstat_named_t *knp;
+
+       ksp = NULL;
+       knp = NULL;
+
+       /* kstats chain already opened by update_kstat (using *kc), let's verify everything went fine. */
+       if ( kc == NULL )
+       {
+               ERROR ("uptime plugin: unable to open kstat control structure");
+               ERROR (INIT_FAILED);
+               return (-1);
+       }
+
+       if (( ksp = kstat_lookup (kc, "unix", 0, "system_misc")) == NULL )
+       {
+               ERROR ("uptime plugin: cannot find %s kstat", "unix:0:system_misc");
+               ERROR (INIT_FAILED);
+               return (-1);
+       }
+
+       if (( kstat_read (kc, ksp, NULL) < 0 ) ||
+               (( knp = (kstat_named_t *) kstat_data_lookup (ksp, "boot_time")) != NULL ))
+       {
+               ERROR ("uptime plugin: kstat data reading failed");
+               ERROR (INIT_FAILED);
+               return (-1);
+       }
+
+       boottime = (time_t) knp->value.ui32;
+
+/* #endif HAVE_LIBKSTAT */
+
+# elif HAVE_SYS_SYSCTL_H
+
+       struct timeval boottv;
+       size_t boottv_len;
+
+        int mib[2];
+
+        mib[0] = CTL_KERN;
+        mib[1] = KERN_BOOTTIME;
+
+        boottv_len = sizeof (boottv);
+
+        if (( sysctl (mib, 2, &boottv, &boottv_len, NULL, 0) != 0 ) || boottv.tv_sec == 0 )
+        {
+                char errbuf[1024];
+                ERROR ("uptime plugin: no value read from sysctl interface: %s",
+                        sstrerror (errno, errbuf, sizeof (errbuf)));
+               ERROR (INIT_FAILED);
+                return (-1);
+        }
+
+       boottime = boottv.tv_sec;
+
+/* #endif HAVE_SYS_SYSCTL_H */
+
+# endif
+
+       return (0);
+
+}
+#endif
+
+static int uptime_read (void)
+{
+       gauge_t uptime;
+
+#if KERNEL_LINUX
+
+       FILE *fh;
+
+       fh = fopen (UPTIME_FILE, "r");
+
+       if (fh == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("uptime plugin: cannot open %s: %s", UPTIME_FILE,
+                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       if ( fscanf (fh, "%lf", &uptime) < 1 )
+       {
+               WARNING ("no value read from %s", UPTIME_FILE);
+               fclose (fh);
+               return (-1);
+       }
+
+       fclose (fh);
+
+/* #endif KERNEL_LINUX */
+
+
+#elif HAVE_SYS_SYSCTL_H || HAVE_LIBKSTAT
+
+       time_t elapsed;
+
+       elapsed = time (NULL) - boottime;
+
+       uptime = (gauge_t) elapsed;
+
+/* #endif HAVE_SYS_SYSCTL_H */
+
+#endif
+
+       uptime_submit (uptime);
+
+       return (0);
+}
+
+void module_register (void)
+{
+#if !KERNEL_LINUX
+       plugin_register_init ("uptime", uptime_init);
+#endif
+       plugin_register_read ("uptime", uptime_read);
+} /* void module_register */