Merge pull request #3329 from efuss/fix-3311
[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 "plugin.h"
25 #include "utils/common/common.h"
26
27 #if KERNEL_LINUX
28 #include <sys/sysinfo.h>
29 /* #endif KERNEL_LINUX */
30
31 #elif HAVE_LIBKSTAT
32 /* Using kstats chain to retrieve the boot time on Solaris / OpenSolaris systems
33  */
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
39  * systems */
40 /* #endif HAVE_SYS_SYSCTL_H */
41
42 #elif HAVE_PERFSTAT
43 #include <libperfstat.h>
44 #include <sys/protosw.h>
45 /* Using perfstat_cpu_total to retrive the boot time in AIX */
46 /* #endif HAVE_PERFSTAT */
47
48 #else
49 #error "No applicable input method."
50 #endif
51
52 /*
53  * Global variables
54  */
55
56 #if HAVE_KSTAT_H
57 #include <kstat.h>
58 #endif
59
60 #if HAVE_LIBKSTAT
61 extern kstat_ctl_t *kc;
62 #endif /* #endif HAVE_LIBKSTAT */
63
64 static void uptime_submit(gauge_t value) {
65   value_list_t vl = VALUE_LIST_INIT;
66
67   vl.values = &(value_t){.gauge = value};
68   vl.values_len = 1;
69
70   sstrncpy(vl.plugin, "uptime", sizeof(vl.plugin));
71   sstrncpy(vl.type, "uptime", sizeof(vl.type));
72
73   plugin_dispatch_values(&vl);
74 }
75
76 /*
77  * On most unix systems the uptime is calculated by looking at the boot
78  * time (stored in unix time, since epoch) and the current one. We are
79  * going to do the same, reading the boot time value while executing
80  * the uptime_init function (there is no need to read, every time the
81  * plugin_read is called, a value that won't change). However, since
82  * uptime_init is run only once, if the function fails in retrieving
83  * the boot time, the plugin is unregistered and there is no chance to
84  * try again later. Nevertheless, this is very unlikely to happen.
85  */
86 static time_t uptime_get_sys(void) { /* {{{ */
87   time_t result;
88 #if KERNEL_LINUX
89   struct sysinfo info;
90   int status;
91
92   status = sysinfo(&info);
93   if (status != 0) {
94     ERROR("uptime plugin: Error calling sysinfo: %s", STRERRNO);
95     return -1;
96   }
97
98   result = (time_t)info.uptime;
99   /* #endif KERNEL_LINUX */
100
101 #elif HAVE_LIBKSTAT
102   kstat_t *ksp;
103   kstat_named_t *knp;
104
105   ksp = NULL;
106   knp = NULL;
107
108   /* kstats chain already opened by update_kstat (using *kc), verify everything
109    * went fine. */
110   if (kc == NULL) {
111     ERROR("uptime plugin: kstat chain control structure not available.");
112     return -1;
113   }
114
115   ksp = kstat_lookup(kc, "unix", 0, "system_misc");
116   if (ksp == NULL) {
117     ERROR("uptime plugin: Cannot find unix:0:system_misc kstat.");
118     return -1;
119   }
120
121   if (kstat_read(kc, ksp, NULL) < 0) {
122     ERROR("uptime plugin: kstat_read failed.");
123     return -1;
124   }
125
126   knp = (kstat_named_t *)kstat_data_lookup(ksp, "boot_time");
127   if (knp == NULL) {
128     ERROR("uptime plugin: kstat_data_lookup (boot_time) failed.");
129     return -1;
130   }
131
132   if (knp->value.ui32 == 0) {
133     ERROR("uptime plugin: kstat_data_lookup returned success, "
134           "but `boottime' is zero!");
135     return -1;
136   }
137
138   result = time(NULL) - (time_t)knp->value.ui32;
139   /* #endif HAVE_LIBKSTAT */
140
141 #elif HAVE_SYS_SYSCTL_H
142   struct timeval boottv = {0};
143   size_t boottv_len;
144   int status;
145
146   int mib[] = {CTL_KERN, KERN_BOOTTIME};
147
148   boottv_len = sizeof(boottv);
149
150   status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &boottv, &boottv_len,
151                   /* new_value = */ NULL, /* new_length = */ 0);
152   if (status != 0) {
153     ERROR("uptime plugin: No value read from sysctl interface: %s", STRERRNO);
154     return -1;
155   }
156
157   if (boottv.tv_sec == 0) {
158     ERROR("uptime plugin: sysctl(3) returned success, "
159           "but `boottime' is zero!");
160     return -1;
161   }
162
163   result = time(NULL) - boottv.tv_sec;
164   /* #endif HAVE_SYS_SYSCTL_H */
165
166 #elif HAVE_PERFSTAT
167   int status;
168   perfstat_cpu_total_t cputotal;
169   int hertz;
170
171   status = perfstat_cpu_total(NULL, &cputotal, sizeof(perfstat_cpu_total_t), 1);
172   if (status < 0) {
173     ERROR("uptime plugin: perfstat_cpu_total: %s", STRERRNO);
174     return -1;
175   }
176
177   hertz = sysconf(_SC_CLK_TCK);
178   if (hertz <= 0)
179     hertz = HZ;
180
181   result = cputotal.lbolt / hertz;
182 #endif /* HAVE_PERFSTAT */
183
184   return result;
185 } /* }}} int uptime_get_sys */
186
187 static int uptime_read(void) {
188   gauge_t uptime;
189   time_t elapsed;
190
191   /* calculate the amount of time elapsed since boot, AKA uptime */
192   elapsed = uptime_get_sys();
193
194   uptime = (gauge_t)elapsed;
195
196   uptime_submit(uptime);
197
198   return 0;
199 }
200
201 void module_register(void) {
202   plugin_register_read("uptime", uptime_read);
203 } /* void module_register */