uptime plugin: Add a plugin to measure the time a system has been running.
[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 #include "common.h"
24 #include "plugin.h"
25
26 #if KERNEL_LINUX
27 # define UPTIME_FILE "/proc/uptime"
28 /*
29    No need for includes, using /proc filesystem, Linux only.
30 */
31 /* #endif KERNEL_LINUX */
32
33 #elif HAVE_LIBKSTAT
34 /* something to include? maybe <sys/sysinfo.h> ? */
35 /*
36    Using kstats chain to retrieve the boot time, this applies to:
37         - Solaris / OpenSolaris
38 */
39 /* #endif HAVE_LIBKSTAT */
40
41 #elif HAVE_SYS_SYSCTL_H
42 # include <sys/sysctl.h>
43 /*
44    Using sysctl interface to retrieve the boot time, this applies to:
45         - *BSD
46         - Darwin / OS X
47 */
48 /* #endif HAVE_SYS_SYSCTL_H */
49
50 #else
51 # error "No applicable input method."
52 #endif
53
54
55 #if !KERNEL_LINUX
56 #define INIT_FAILED "uptime plugin: unable to calculate uptime, the plugin will be disabled."
57 #endif
58
59
60 #if KERNEL_LINUX
61 /* global variables not needed*/
62
63 #elif HAVE_SYS_SYSCTL_H || HAVE_LIBKSTAT
64 static time_t boottime;
65 # if HAVE_LIBKSTAT
66 extern kstat_ctl_t *kc;
67 # endif
68
69 #endif
70
71
72 static void uptime_submit (gauge_t uptime)
73 {
74         value_t values[1];
75         value_list_t vl = VALUE_LIST_INIT;
76
77         values[0].gauge = uptime;
78
79         vl.values = values;
80         vl.values_len = 1;
81         vl.time = time (NULL);
82
83         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
84         sstrncpy (vl.plugin, "uptime", sizeof (vl.plugin));
85         sstrncpy (vl.type, "uptime", sizeof (vl.type));
86
87         plugin_dispatch_values (&vl);
88 }
89
90 #if !KERNEL_LINUX
91 static int uptime_init (void)
92 {
93 /*      NOTE
94
95         On unix systems other than Linux there is no /proc filesystem which
96         calculates the uptime every time we call a read for the /proc/uptime
97         file, the only information available is the boot time (in unix time,
98         since epoch). Hence there is no need to read, every time the
99         plugin_read is called, a value that won't change: this is a right
100         task for the uptime_init function. However, since uptime_init is run
101         only once, if the function fails in retrieving the boot time, the
102         plugin is unregistered and there is no chance to try again later.
103         Nevertheless, this is very unlikely to happen.
104
105 */
106
107 # if HAVE_LIBKSTAT
108
109         kstat_t *ksp;
110         kstat_named_t *knp;
111
112         ksp = NULL;
113         knp = NULL;
114
115         /* kstats chain already opened by update_kstat (using *kc), let's verify everything went fine. */
116         if ( kc == NULL )
117         {
118                 ERROR ("uptime plugin: unable to open kstat control structure");
119                 ERROR (INIT_FAILED);
120                 return (-1);
121         }
122
123         if (( ksp = kstat_lookup (kc, "unix", 0, "system_misc")) == NULL )
124         {
125                 ERROR ("uptime plugin: cannot find %s kstat", "unix:0:system_misc");
126                 ERROR (INIT_FAILED);
127                 return (-1);
128         }
129
130         if (( kstat_read (kc, ksp, NULL) < 0 ) ||
131                 (( knp = (kstat_named_t *) kstat_data_lookup (ksp, "boot_time")) != NULL ))
132         {
133                 ERROR ("uptime plugin: kstat data reading failed");
134                 ERROR (INIT_FAILED);
135                 return (-1);
136         }
137
138         boottime = (time_t) knp->value.ui32;
139
140 /* #endif HAVE_LIBKSTAT */
141
142 # elif HAVE_SYS_SYSCTL_H
143
144         struct timeval boottv;
145         size_t boottv_len;
146
147         int mib[2];
148
149         mib[0] = CTL_KERN;
150         mib[1] = KERN_BOOTTIME;
151
152         boottv_len = sizeof (boottv);
153
154         if (( sysctl (mib, 2, &boottv, &boottv_len, NULL, 0) != 0 ) || boottv.tv_sec == 0 )
155         {
156                 char errbuf[1024];
157                 ERROR ("uptime plugin: no value read from sysctl interface: %s",
158                         sstrerror (errno, errbuf, sizeof (errbuf)));
159                 ERROR (INIT_FAILED);
160                 return (-1);
161         }
162
163         boottime = boottv.tv_sec;
164
165 /* #endif HAVE_SYS_SYSCTL_H */
166
167 # endif
168
169         return (0);
170
171 }
172 #endif
173
174 static int uptime_read (void)
175 {
176         gauge_t uptime;
177
178 #if KERNEL_LINUX
179
180         FILE *fh;
181
182         fh = fopen (UPTIME_FILE, "r");
183
184         if (fh == NULL)
185         {
186                 char errbuf[1024];
187                 ERROR ("uptime plugin: cannot open %s: %s", UPTIME_FILE,
188                         sstrerror (errno, errbuf, sizeof (errbuf)));
189                 return (-1);
190         }
191
192         if ( fscanf (fh, "%lf", &uptime) < 1 )
193         {
194                 WARNING ("no value read from %s", UPTIME_FILE);
195                 fclose (fh);
196                 return (-1);
197         }
198
199         fclose (fh);
200
201 /* #endif KERNEL_LINUX */
202
203
204 #elif HAVE_SYS_SYSCTL_H || HAVE_LIBKSTAT
205
206         time_t elapsed;
207
208         elapsed = time (NULL) - boottime;
209
210         uptime = (gauge_t) elapsed;
211
212 /* #endif HAVE_SYS_SYSCTL_H */
213
214 #endif
215
216         uptime_submit (uptime);
217
218         return (0);
219 }
220
221 void module_register (void)
222 {
223 #if !KERNEL_LINUX
224         plugin_register_init ("uptime", uptime_init);
225 #endif
226         plugin_register_read ("uptime", uptime_read);
227 } /* void module_register */