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"
24 #include "common.h"
25 #include "plugin.h"
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 */
32 #elif HAVE_LIBKSTAT
33 /* Using kstats chain to retrieve the boot time on Solaris / OpenSolaris systems */
34 /* #endif HAVE_LIBKSTAT */
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 */
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 */
47 #else
48 # error "No applicable input method."
49 #endif
51 /*
52 * Global variables
53 */
54 /* boottime always used, no OS distinction */
55 static time_t boottime;
57 #if HAVE_LIBKSTAT
58 extern kstat_ctl_t *kc;
59 #endif /* #endif HAVE_LIBKSTAT */
61 static void uptime_submit (gauge_t value)
62 {
63 value_list_t vl = VALUE_LIST_INIT;
65 vl.values = &(value_t) { .gauge = value };
66 vl.values_len = 1;
68 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
69 sstrncpy (vl.plugin, "uptime", sizeof (vl.plugin));
70 sstrncpy (vl.type, "uptime", sizeof (vl.type));
72 plugin_dispatch_values (&vl);
73 }
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 */
88 #if KERNEL_LINUX
89 unsigned long starttime;
90 char buffer[1024];
91 int ret;
92 FILE *fh;
94 ret = 0;
96 fh = fopen (STAT_FILE, "r");
98 if (fh == NULL)
99 {
100 char errbuf[1024];
101 ERROR ("uptime plugin: Cannot open "STAT_FILE": %s",
102 sstrerror (errno, errbuf, sizeof (errbuf)));
103 return (-1);
104 }
106 while (fgets (buffer, 1024, fh) != NULL)
107 {
108 /* look for the btime string and read the value */
109 ret = sscanf (buffer, "btime %lu", &starttime);
110 /* avoid further loops if btime has been found and read
111 * correctly (hopefully) */
112 if (ret == 1)
113 break;
114 }
116 fclose (fh);
118 /* loop done, check if no value has been found/read */
119 if (ret != 1)
120 {
121 ERROR ("uptime plugin: No value read from "STAT_FILE"");
122 return (-1);
123 }
125 boottime = (time_t) starttime;
127 if (boottime == 0)
128 {
129 ERROR ("uptime plugin: btime read from "STAT_FILE", "
130 "but `boottime' is zero!");
131 return (-1);
132 }
133 /* #endif KERNEL_LINUX */
135 #elif HAVE_LIBKSTAT
136 kstat_t *ksp;
137 kstat_named_t *knp;
139 ksp = NULL;
140 knp = NULL;
142 /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
143 if (kc == NULL)
144 {
145 ERROR ("uptime plugin: kstat chain control structure not available.");
146 return (-1);
147 }
149 ksp = kstat_lookup (kc, "unix", 0, "system_misc");
150 if (ksp == NULL)
151 {
152 ERROR ("uptime plugin: Cannot find unix:0:system_misc kstat.");
153 return (-1);
154 }
156 if (kstat_read (kc, ksp, NULL) < 0)
157 {
158 ERROR ("uptime plugin: kstat_read failed.");
159 return (-1);
160 }
162 knp = (kstat_named_t *) kstat_data_lookup (ksp, "boot_time");
163 if (knp == NULL)
164 {
165 ERROR ("uptime plugin: kstat_data_lookup (boot_time) failed.");
166 return (-1);
167 }
169 boottime = (time_t) knp->value.ui32;
171 if (boottime == 0)
172 {
173 ERROR ("uptime plugin: kstat_data_lookup returned success, "
174 "but `boottime' is zero!");
175 return (-1);
176 }
177 /* #endif HAVE_LIBKSTAT */
179 # elif HAVE_SYS_SYSCTL_H
180 struct timeval boottv = { 0 };
181 size_t boottv_len;
182 int status;
184 int mib[] = { CTL_KERN, KERN_BOOTTIME };
186 boottv_len = sizeof (boottv);
188 status = sysctl (mib, STATIC_ARRAY_SIZE (mib), &boottv, &boottv_len,
189 /* new_value = */ NULL, /* new_length = */ 0);
190 if (status != 0)
191 {
192 char errbuf[1024];
193 ERROR ("uptime plugin: No value read from sysctl interface: %s",
194 sstrerror (errno, errbuf, sizeof (errbuf)));
195 return (-1);
196 }
198 boottime = boottv.tv_sec;
200 if (boottime == 0)
201 {
202 ERROR ("uptime plugin: sysctl(3) returned success, "
203 "but `boottime' is zero!");
204 return (-1);
205 }
206 /* #endif HAVE_SYS_SYSCTL_H */
208 #elif HAVE_PERFSTAT
209 int status;
210 perfstat_cpu_total_t cputotal;
211 int hertz;
213 status = perfstat_cpu_total(NULL, &cputotal,
214 sizeof(perfstat_cpu_total_t), 1);
215 if (status < 0)
216 {
217 char errbuf[1024];
218 ERROR ("uptime plugin: perfstat_cpu_total: %s",
219 sstrerror (errno, errbuf, sizeof (errbuf)));
220 return (-1);
221 }
223 hertz = sysconf(_SC_CLK_TCK);
224 if (hertz <= 0)
225 hertz = HZ;
227 boottime = time(NULL) - cputotal.lbolt / hertz;
228 #endif /* HAVE_PERFSTAT */
230 return (0);
231 } /* }}} int uptime_init */
233 static int uptime_read (void)
234 {
235 gauge_t uptime;
236 time_t elapsed;
238 /* calculate the amount of time elapsed since boot, AKA uptime */
239 elapsed = time (NULL) - boottime;
241 uptime = (gauge_t) elapsed;
243 uptime_submit (uptime);
245 return (0);
246 }
248 void module_register (void)
249 {
250 plugin_register_init ("uptime", uptime_init);
251 plugin_register_read ("uptime", uptime_read);
252 } /* void module_register */