Merge branch 'collectd-5.7' into collectd-5.8
[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 #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 #if HAVE_KSTAT_H
56 #include <kstat.h>
57 #endif
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 /*
76  * On most unix systems the uptime is calculated by looking at the boot
77  * time (stored in unix time, since epoch) and the current one. We are
78  * going to do the same, reading the boot time value while executing
79  * the uptime_init function (there is no need to read, every time the
80  * plugin_read is called, a value that won't change). However, since
81  * uptime_init is run only once, if the function fails in retrieving
82  * the boot time, the plugin is unregistered and there is no chance to
83  * try again later. Nevertheless, this is very unlikely to happen.
84  */
85 static time_t uptime_get_sys(void) { /* {{{ */
86   time_t result;
87 #if KERNEL_LINUX
88   struct sysinfo info;
89   int status;
90
91   status = sysinfo(&info);
92   if (status != 0) {
93     char errbuf[1024];
94     ERROR("uptime plugin: Error calling sysinfo: %s",
95           sstrerror(errno, errbuf, sizeof(errbuf)));
96     return -1;
97   }
98
99   result = (time_t)info.uptime;
100 /* #endif KERNEL_LINUX */
101
102 #elif HAVE_LIBKSTAT
103   kstat_t *ksp;
104   kstat_named_t *knp;
105
106   ksp = NULL;
107   knp = NULL;
108
109   /* kstats chain already opened by update_kstat (using *kc), verify everything
110    * went fine. */
111   if (kc == NULL) {
112     ERROR("uptime plugin: kstat chain control structure not available.");
113     return -1;
114   }
115
116   ksp = kstat_lookup(kc, "unix", 0, "system_misc");
117   if (ksp == NULL) {
118     ERROR("uptime plugin: Cannot find unix:0:system_misc kstat.");
119     return -1;
120   }
121
122   if (kstat_read(kc, ksp, NULL) < 0) {
123     ERROR("uptime plugin: kstat_read failed.");
124     return -1;
125   }
126
127   knp = (kstat_named_t *)kstat_data_lookup(ksp, "boot_time");
128   if (knp == NULL) {
129     ERROR("uptime plugin: kstat_data_lookup (boot_time) failed.");
130     return -1;
131   }
132
133   if (knp->value.ui32 == 0) {
134     ERROR("uptime plugin: kstat_data_lookup returned success, "
135           "but `boottime' is zero!");
136     return -1;
137   }
138
139   result = time(NULL) - (time_t)knp->value.ui32;
140 /* #endif HAVE_LIBKSTAT */
141
142 #elif HAVE_SYS_SYSCTL_H
143   struct timeval boottv = {0};
144   size_t boottv_len;
145   int status;
146
147   int mib[] = {CTL_KERN, KERN_BOOTTIME};
148
149   boottv_len = sizeof(boottv);
150
151   status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &boottv, &boottv_len,
152                   /* new_value = */ NULL, /* new_length = */ 0);
153   if (status != 0) {
154     char errbuf[1024];
155     ERROR("uptime plugin: No value read from sysctl interface: %s",
156           sstrerror(errno, errbuf, sizeof(errbuf)));
157     return -1;
158   }
159
160   if (boottv.tv_sec == 0) {
161     ERROR("uptime plugin: sysctl(3) returned success, "
162           "but `boottime' is zero!");
163     return -1;
164   }
165
166   result = time(NULL) - boottv.tv_sec;
167 /* #endif HAVE_SYS_SYSCTL_H */
168
169 #elif HAVE_PERFSTAT
170   int status;
171   perfstat_cpu_total_t cputotal;
172   int hertz;
173
174   status = perfstat_cpu_total(NULL, &cputotal, sizeof(perfstat_cpu_total_t), 1);
175   if (status < 0) {
176     char errbuf[1024];
177     ERROR("uptime plugin: perfstat_cpu_total: %s",
178           sstrerror(errno, errbuf, sizeof(errbuf)));
179     return -1;
180   }
181
182   hertz = sysconf(_SC_CLK_TCK);
183   if (hertz <= 0)
184     hertz = HZ;
185
186   result = cputotal.lbolt / hertz;
187 #endif /* HAVE_PERFSTAT */
188
189   return result;
190 } /* }}} int uptime_get_sys */
191
192 static int uptime_read(void) {
193   gauge_t uptime;
194   time_t elapsed;
195
196   /* calculate the amount of time elapsed since boot, AKA uptime */
197   elapsed = uptime_get_sys();
198
199   uptime = (gauge_t)elapsed;
200
201   uptime_submit(uptime);
202
203   return 0;
204 }
205
206 void module_register(void) {
207   plugin_register_read("uptime", uptime_read);
208 } /* void module_register */