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