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