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