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