Merge branch 'collectd-5.5' into collectd-5.6
[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  **/
21
22 #include "collectd.h"
23
24 #include "common.h"
25 #include "plugin.h"
26
27 #if KERNEL_LINUX
28 # define STAT_FILE "/proc/stat"
29 /* Using /proc filesystem to retrieve the boot time, Linux only. */
30 /* #endif KERNEL_LINUX */
31
32 #elif HAVE_LIBKSTAT
33 /* Using kstats chain to retrieve the boot time on Solaris / OpenSolaris systems */
34 /* #endif HAVE_LIBKSTAT */
35
36 #elif HAVE_SYS_SYSCTL_H
37 # include <sys/sysctl.h>
38 /* Using sysctl interface to retrieve the boot time on *BSD / Darwin / OS X systems */
39 /* #endif HAVE_SYS_SYSCTL_H */
40
41 #elif HAVE_PERFSTAT
42 # include <sys/protosw.h>
43 # include <libperfstat.h>
44 /* Using perfstat_cpu_total to retrive the boot time in AIX */
45 /* #endif HAVE_PERFSTAT */
46
47 #else
48 # error "No applicable input method."
49 #endif
50
51 /*
52  * Global variables
53  */
54 /* boottime always used, no OS distinction */
55 static time_t boottime;
56
57 #if HAVE_LIBKSTAT
58 extern kstat_ctl_t *kc;
59 #endif /* #endif HAVE_LIBKSTAT */
60
61 static void uptime_submit (gauge_t uptime)
62 {
63         value_t values[1];
64         value_list_t vl = VALUE_LIST_INIT;
65
66         values[0].gauge = uptime;
67
68         vl.values = values;
69         vl.values_len = 1;
70
71         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
72         sstrncpy (vl.plugin, "uptime", sizeof (vl.plugin));
73         sstrncpy (vl.type, "uptime", sizeof (vl.type));
74
75         plugin_dispatch_values (&vl);
76 }
77
78 static int uptime_init (void) /* {{{ */
79 {
80         /*
81          * On most unix systems the uptime is calculated by looking at the boot
82          * time (stored in unix time, since epoch) and the current one. We are
83          * going to do the same, reading the boot time value while executing
84          * the uptime_init function (there is no need to read, every time the
85          * plugin_read is called, a value that won't change). However, since
86          * uptime_init is run only once, if the function fails in retrieving
87          * the boot time, the plugin is unregistered and there is no chance to
88          * try again later. Nevertheless, this is very unlikely to happen.
89          */
90
91 #if KERNEL_LINUX
92         unsigned long starttime;
93         char buffer[1024];
94         int ret;
95         FILE *fh;
96
97         ret = 0;
98
99         fh = fopen (STAT_FILE, "r");
100
101         if (fh == NULL)
102         {
103                 char errbuf[1024];
104                 ERROR ("uptime plugin: Cannot open "STAT_FILE": %s",
105                         sstrerror (errno, errbuf, sizeof (errbuf)));
106                 return (-1);
107         }
108
109         while (fgets (buffer, 1024, fh) != NULL)
110         {
111                 /* look for the btime string and read the value */
112                 ret = sscanf (buffer, "btime %lu", &starttime);
113                 /* avoid further loops if btime has been found and read
114                  * correctly (hopefully) */
115                 if (ret == 1)
116                         break;
117         }
118
119         fclose (fh);
120
121         /* loop done, check if no value has been found/read */
122         if (ret != 1)
123         {
124                 ERROR ("uptime plugin: No value read from "STAT_FILE"");
125                 return (-1);
126         }
127
128         boottime = (time_t) starttime;
129
130         if (boottime == 0)
131         {
132                 ERROR ("uptime plugin: btime read from "STAT_FILE", "
133                                 "but `boottime' is zero!");
134                 return (-1);
135         }
136 /* #endif KERNEL_LINUX */
137
138 #elif HAVE_LIBKSTAT
139         kstat_t *ksp;
140         kstat_named_t *knp;
141
142         ksp = NULL;
143         knp = NULL;
144
145         /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
146         if (kc == NULL)
147         {
148                 ERROR ("uptime plugin: kstat chain control structure not available.");
149                 return (-1);
150         }
151
152         ksp = kstat_lookup (kc, "unix", 0, "system_misc");
153         if (ksp == NULL)
154         {
155                 ERROR ("uptime plugin: Cannot find unix:0:system_misc kstat.");
156                 return (-1);
157         }
158
159         if (kstat_read (kc, ksp, NULL) < 0)
160         {
161                 ERROR ("uptime plugin: kstat_read failed.");
162                 return (-1);
163         }
164
165         knp = (kstat_named_t *) kstat_data_lookup (ksp, "boot_time");
166         if (knp == NULL)
167         {
168                 ERROR ("uptime plugin: kstat_data_lookup (boot_time) failed.");
169                 return (-1);
170         }
171
172         boottime = (time_t) knp->value.ui32;
173
174         if (boottime == 0)
175         {
176                 ERROR ("uptime plugin: kstat_data_lookup returned success, "
177                         "but `boottime' is zero!");
178                 return (-1);
179         }
180 /* #endif HAVE_LIBKSTAT */
181
182 # elif HAVE_SYS_SYSCTL_H
183         struct timeval boottv = { 0 };
184         size_t boottv_len;
185         int status;
186
187         int mib[] = { CTL_KERN, KERN_BOOTTIME };
188
189         boottv_len = sizeof (boottv);
190
191         status = sysctl (mib, STATIC_ARRAY_SIZE (mib), &boottv, &boottv_len,
192                         /* new_value = */ NULL, /* new_length = */ 0);
193         if (status != 0)
194         {
195                 char errbuf[1024];
196                 ERROR ("uptime plugin: No value read from sysctl interface: %s",
197                         sstrerror (errno, errbuf, sizeof (errbuf)));
198                 return (-1);
199         }
200
201         boottime = boottv.tv_sec;
202
203         if (boottime == 0)
204         {
205                 ERROR ("uptime plugin: sysctl(3) returned success, "
206                                 "but `boottime' is zero!");
207                 return (-1);
208         }
209 /* #endif HAVE_SYS_SYSCTL_H */
210
211 #elif HAVE_PERFSTAT
212         int status;
213         perfstat_cpu_total_t cputotal;
214         int hertz;
215
216         status = perfstat_cpu_total(NULL, &cputotal,
217                 sizeof(perfstat_cpu_total_t), 1);
218         if (status < 0)
219         {
220                 char errbuf[1024];
221                 ERROR ("uptime plugin: perfstat_cpu_total: %s",
222                         sstrerror (errno, errbuf, sizeof (errbuf)));
223                 return (-1);
224         }
225
226         hertz = sysconf(_SC_CLK_TCK);
227         if (hertz <= 0)
228                 hertz = HZ;
229
230         boottime = time(NULL) - cputotal.lbolt / hertz;
231 #endif /* HAVE_PERFSTAT */
232
233         return (0);
234 } /* }}} int uptime_init */
235
236 static int uptime_read (void)
237 {
238         gauge_t uptime;
239         time_t elapsed;
240
241         /* calculate the amount of time elapsed since boot, AKA uptime */
242         elapsed = time (NULL) - boottime;
243
244         uptime = (gauge_t) elapsed;
245
246         uptime_submit (uptime);
247
248         return (0);
249 }
250
251 void module_register (void)
252 {
253         plugin_register_init ("uptime", uptime_init);
254         plugin_register_read ("uptime", uptime_read);
255 } /* void module_register */