Code

Merge remote-tracking branch 'github/pr/387'
[collectd.git] / src / uptime.c
1 /**
2  * collectd - src/uptime.c
3  * Copyright (C) 2009   Marco Chiappero
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Marco Chiappero <marco at absence.it>
20  **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
26 #if KERNEL_LINUX
27 # define STAT_FILE "/proc/stat"
28 /* Using /proc filesystem to retrieve the boot time, Linux only. */
29 /* #endif KERNEL_LINUX */
31 #elif HAVE_LIBKSTAT
32 /* Using kstats chain to retrieve the boot time on Solaris / OpenSolaris systems */
33 /* #endif HAVE_LIBKSTAT */
35 #elif HAVE_SYS_SYSCTL_H
36 # include <sys/sysctl.h>
37 /* Using sysctl interface to retrieve the boot time on *BSD / Darwin / OS X systems */
38 /* #endif HAVE_SYS_SYSCTL_H */
40 #elif HAVE_PERFSTAT
41 # include <sys/protosw.h>
42 # include <libperfstat.h>
43 /* Using perfstat_cpu_total to retrive the boot time in AIX */
44 /* #endif HAVE_PERFSTAT */
46 #else
47 # error "No applicable input method."
48 #endif
50 /*
51  * Global variables
52  */
53 /* boottime always used, no OS distinction */
54 static time_t boottime;
56 #if HAVE_LIBKSTAT
57 extern kstat_ctl_t *kc;
58 #endif /* #endif HAVE_LIBKSTAT */
60 static void uptime_submit (gauge_t uptime)
61 {
62         value_t values[1];
63         value_list_t vl = VALUE_LIST_INIT;
65         values[0].gauge = uptime;
67         vl.values = values;
68         vl.values_len = 1;
70         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
71         sstrncpy (vl.plugin, "uptime", sizeof (vl.plugin));
72         sstrncpy (vl.type, "uptime", sizeof (vl.type));
74         plugin_dispatch_values (&vl);
75 }
77 static int uptime_init (void) /* {{{ */
78 {
79         /*
80          * On most unix systems the uptime is calculated by looking at the boot
81          * time (stored in unix time, since epoch) and the current one. We are
82          * going to do the same, reading the boot time value while executing
83          * the uptime_init function (there is no need to read, every time the
84          * plugin_read is called, a value that won't change). However, since
85          * uptime_init is run only once, if the function fails in retrieving
86          * the boot time, the plugin is unregistered and there is no chance to
87          * try again later. Nevertheless, this is very unlikely to happen.
88          */
90 #if KERNEL_LINUX
91         unsigned long starttime;
92         char buffer[1024];
93         int ret;
94         FILE *fh;
96         ret = 0;
98         fh = fopen (STAT_FILE, "r");
100         if (fh == NULL)
101         {
102                 char errbuf[1024];
103                 ERROR ("uptime plugin: Cannot open "STAT_FILE": %s",
104                         sstrerror (errno, errbuf, sizeof (errbuf)));
105                 return (-1);
106         }
108         while (fgets (buffer, 1024, fh) != NULL)
109         {
110                 /* look for the btime string and read the value */
111                 ret = sscanf (buffer, "btime %lu", &starttime);
112                 /* avoid further loops if btime has been found and read
113                  * correctly (hopefully) */
114                 if (ret == 1)
115                         break;
116         }
118         fclose (fh);
120         /* loop done, check if no value has been found/read */
121         if (ret != 1)
122         {
123                 ERROR ("uptime plugin: No value read from "STAT_FILE"");
124                 return (-1);
125         }
127         boottime = (time_t) starttime;
129         if (boottime == 0)
130         {
131                 ERROR ("uptime plugin: btime read from "STAT_FILE", "
132                                 "but `boottime' is zero!");
133                 return (-1);
134         }
135 /* #endif KERNEL_LINUX */
137 #elif HAVE_LIBKSTAT
138         kstat_t *ksp;
139         kstat_named_t *knp;
141         ksp = NULL;
142         knp = NULL;
144         /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
145         if (kc == NULL)
146         {
147                 ERROR ("uptime plugin: kstat chain control structure not available.");
148                 return (-1);
149         }
151         ksp = kstat_lookup (kc, "unix", 0, "system_misc");
152         if (ksp == NULL)
153         {
154                 ERROR ("uptime plugin: Cannot find unix:0:system_misc kstat.");
155                 return (-1);
156         }
158         if (kstat_read (kc, ksp, NULL) < 0)
159         {
160                 ERROR ("uptime plugin: kstat_read failed.");
161                 return (-1);
162         }
164         knp = (kstat_named_t *) kstat_data_lookup (ksp, "boot_time");
165         if (knp == NULL)
166         {
167                 ERROR ("uptime plugin: kstat_data_lookup (boot_time) failed.");
168                 return (-1);
169         }
171         boottime = (time_t) knp->value.ui32;
173         if (boottime == 0)
174         {
175                 ERROR ("uptime plugin: kstat_data_lookup returned success, "
176                         "but `boottime' is zero!");
177                 return (-1);
178         }
179 /* #endif HAVE_LIBKSTAT */
181 # elif HAVE_SYS_SYSCTL_H
182         struct timeval boottv;
183         size_t boottv_len;
184         int status;
186         int mib[2];
188         mib[0] = CTL_KERN;
189         mib[1] = KERN_BOOTTIME;
191         boottv_len = sizeof (boottv);
192         memset (&boottv, 0, boottv_len);
194         status = sysctl (mib, STATIC_ARRAY_SIZE (mib), &boottv, &boottv_len,
195                         /* new_value = */ NULL, /* new_length = */ 0);
196         if (status != 0)
197         {
198                 char errbuf[1024];
199                 ERROR ("uptime plugin: No value read from sysctl interface: %s",
200                         sstrerror (errno, errbuf, sizeof (errbuf)));
201                 return (-1);
202         }
204         boottime = boottv.tv_sec;
206         if (boottime == 0)
207         {
208                 ERROR ("uptime plugin: sysctl(3) returned success, "
209                                 "but `boottime' is zero!");
210                 return (-1);
211         }
212 /* #endif HAVE_SYS_SYSCTL_H */
214 #elif HAVE_PERFSTAT
215         int status;
216         perfstat_cpu_total_t cputotal;
217         int hertz;
219         status = perfstat_cpu_total(NULL, &cputotal,
220                 sizeof(perfstat_cpu_total_t), 1);
221         if (status < 0)
222         {
223                 char errbuf[1024];
224                 ERROR ("uptime plugin: perfstat_cpu_total: %s",
225                         sstrerror (errno, errbuf, sizeof (errbuf)));
226                 return (-1);
227         }
229         hertz = sysconf(_SC_CLK_TCK);
230         if (hertz <= 0)
231                 hertz = HZ;
233         boottime = time(NULL) - cputotal.lbolt / hertz;
234 #endif /* HAVE_PERFSTAT */
236         return (0);
237 } /* }}} int uptime_init */
239 static int uptime_read (void)
241         gauge_t uptime;
242         time_t elapsed;
244         /* calculate the ammount of time elapsed since boot, AKA uptime */
245         elapsed = time (NULL) - boottime;
247         uptime = (gauge_t) elapsed;
249         uptime_submit (uptime);
251         return (0);
254 void module_register (void)
256         plugin_register_init ("uptime", uptime_init);
257         plugin_register_read ("uptime", uptime_read);
258 } /* void module_register */