f38599dc7b32be95dbad169a98822fabbeff286e
1 /**
2 * collectd - src/csv.c
3 * Copyright (C) 2007-2009 Florian octo Forster
4 * Copyright (C) 2009 Doug MacEachern
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; only version 2 of the License is applicable.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Florian octo Forster <octo at verplant.org>
21 * Doug MacEachern <dougm@hyperic.com>
22 **/
24 #include "collectd.h"
25 #include "plugin.h"
26 #include "common.h"
27 #include "utils_cache.h"
28 #include "utils_parse_option.h"
30 /*
31 * Private variables
32 */
33 static const char *config_keys[] =
34 {
35 "DataDir",
36 "StoreRates"
37 };
38 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
40 static char *datadir = NULL;
41 static int store_rates = 0;
42 static int use_stdio = 0;
44 static int value_list_to_string (char *buffer, int buffer_len,
45 const data_set_t *ds, const value_list_t *vl)
46 {
47 int offset;
48 int status;
49 int i;
50 gauge_t *rates = NULL;
52 assert (0 == strcmp (ds->type, vl->type));
54 memset (buffer, '\0', buffer_len);
56 status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
57 if ((status < 1) || (status >= buffer_len))
58 return (-1);
59 offset = status;
61 for (i = 0; i < ds->ds_num; i++)
62 {
63 if ((ds->ds[i].type != DS_TYPE_COUNTER)
64 && (ds->ds[i].type != DS_TYPE_GAUGE)
65 && (ds->ds[i].type != DS_TYPE_DERIVE)
66 && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
67 return (-1);
69 if (ds->ds[i].type == DS_TYPE_COUNTER)
70 {
71 if (store_rates == 0)
72 {
73 status = ssnprintf (buffer + offset,
74 buffer_len - offset,
75 ",%llu",
76 vl->values[i].counter);
77 }
78 else /* if (store_rates == 1) */
79 {
80 if (rates == NULL)
81 rates = uc_get_rate (ds, vl);
82 if (rates == NULL)
83 {
84 WARNING ("csv plugin: "
85 "uc_get_rate failed.");
86 return (-1);
87 }
88 status = ssnprintf (buffer + offset,
89 buffer_len - offset,
90 ",%lf", rates[i]);
91 }
92 }
93 else if (ds->ds[i].type == DS_TYPE_GAUGE)
94 {
95 status = ssnprintf (buffer + offset, buffer_len - offset,
96 ",%lf", vl->values[i].gauge);
97 }
98 else if (ds->ds[i].type == DS_TYPE_DERIVE)
99 {
100 if (store_rates == 0)
101 {
102 status = ssnprintf (buffer + offset,
103 buffer_len - offset,
104 ",%llu",
105 vl->values[i].derive);
106 }
107 else /* if (store_rates == 1) */
108 {
109 if (rates == NULL)
110 rates = uc_get_rate (ds, vl);
111 if (rates == NULL)
112 {
113 WARNING ("csv plugin: "
114 "uc_get_rate failed.");
115 return (-1);
116 }
117 status = ssnprintf (buffer + offset,
118 buffer_len - offset,
119 ",%lf", rates[i]);
120 }
121 }
122 else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
123 {
124 if (store_rates == 0)
125 {
126 status = ssnprintf (buffer + offset,
127 buffer_len - offset,
128 ",%llu",
129 vl->values[i].absolute);
130 }
131 else /* if (store_rates == 1) */
132 {
133 if (rates == NULL)
134 rates = uc_get_rate (ds, vl);
135 if (rates == NULL)
136 {
137 WARNING ("csv plugin: "
138 "uc_get_rate failed.");
139 return (-1);
140 }
141 status = ssnprintf (buffer + offset,
142 buffer_len - offset,
143 ",%lf", rates[i]);
144 }
145 }
147 if ((status < 1) || (status >= (buffer_len - offset)))
148 {
149 sfree (rates);
150 return (-1);
151 }
153 offset += status;
154 } /* for ds->ds_num */
156 sfree (rates);
157 return (0);
158 } /* int value_list_to_string */
160 static int value_list_to_filename (char *buffer, int buffer_len,
161 const data_set_t *ds, const value_list_t *vl)
162 {
163 int offset = 0;
164 int status;
166 assert (0 == strcmp (ds->type, vl->type));
168 if (datadir != NULL)
169 {
170 status = ssnprintf (buffer + offset, buffer_len - offset,
171 "%s/", datadir);
172 if ((status < 1) || (status >= buffer_len - offset))
173 return (-1);
174 offset += status;
175 }
177 status = ssnprintf (buffer + offset, buffer_len - offset,
178 "%s/", vl->host);
179 if ((status < 1) || (status >= buffer_len - offset))
180 return (-1);
181 offset += status;
183 if (strlen (vl->plugin_instance) > 0)
184 status = ssnprintf (buffer + offset, buffer_len - offset,
185 "%s-%s/", vl->plugin, vl->plugin_instance);
186 else
187 status = ssnprintf (buffer + offset, buffer_len - offset,
188 "%s/", vl->plugin);
189 if ((status < 1) || (status >= buffer_len - offset))
190 return (-1);
191 offset += status;
193 if (strlen (vl->type_instance) > 0)
194 status = ssnprintf (buffer + offset, buffer_len - offset,
195 "%s-%s", vl->type, vl->type_instance);
196 else
197 status = ssnprintf (buffer + offset, buffer_len - offset,
198 "%s", vl->type);
199 if ((status < 1) || (status >= buffer_len - offset))
200 return (-1);
201 offset += status;
203 if (!use_stdio)
204 {
205 time_t now;
206 struct tm stm;
208 /* TODO: Find a way to minimize the calls to `localtime_r',
209 * since they are pretty expensive.. */
210 now = time (NULL);
211 if (localtime_r (&now, &stm) == NULL)
212 {
213 ERROR ("csv plugin: localtime_r failed");
214 return (1);
215 }
217 strftime (buffer + offset, buffer_len - offset,
218 "-%Y-%m-%d", &stm);
219 }
221 return (0);
222 } /* int value_list_to_filename */
224 static int csv_create_file (const char *filename, const data_set_t *ds)
225 {
226 FILE *csv;
227 int i;
229 if (check_create_dir (filename))
230 return (-1);
232 csv = fopen (filename, "w");
233 if (csv == NULL)
234 {
235 char errbuf[1024];
236 ERROR ("csv plugin: fopen (%s) failed: %s",
237 filename,
238 sstrerror (errno, errbuf, sizeof (errbuf)));
239 return (-1);
240 }
242 fprintf (csv, "epoch");
243 for (i = 0; i < ds->ds_num; i++)
244 fprintf (csv, ",%s", ds->ds[i].name);
246 fprintf (csv, "\n");
247 fclose (csv);
249 return 0;
250 } /* int csv_create_file */
252 static int csv_config (const char *key, const char *value)
253 {
254 if (strcasecmp ("DataDir", key) == 0)
255 {
256 if (datadir != NULL)
257 free (datadir);
258 if (strcasecmp ("stdout", value) == 0)
259 {
260 use_stdio = 1;
261 return (0);
262 }
263 else if (strcasecmp ("stderr", value) == 0)
264 {
265 use_stdio = 2;
266 return (0);
267 }
268 datadir = strdup (value);
269 if (datadir != NULL)
270 {
271 int len = strlen (datadir);
272 while ((len > 0) && (datadir[len - 1] == '/'))
273 {
274 len--;
275 datadir[len] = '\0';
276 }
277 if (len <= 0)
278 {
279 free (datadir);
280 datadir = NULL;
281 }
282 }
283 }
284 else if (strcasecmp ("StoreRates", key) == 0)
285 {
286 if ((strcasecmp ("True", value) == 0)
287 || (strcasecmp ("Yes", value) == 0)
288 || (strcasecmp ("On", value) == 0))
289 {
290 store_rates = 1;
291 }
292 else
293 {
294 store_rates = 0;
295 }
296 }
297 else
298 {
299 return (-1);
300 }
301 return (0);
302 } /* int csv_config */
304 static int csv_write (const data_set_t *ds, const value_list_t *vl,
305 user_data_t __attribute__((unused)) *user_data)
306 {
307 struct stat statbuf;
308 char filename[512];
309 char values[512];
310 FILE *csv;
311 int csv_fd;
312 struct flock fl;
313 int status;
315 if (0 != strcmp (ds->type, vl->type)) {
316 ERROR ("csv plugin: DS type does not match value list type");
317 return -1;
318 }
320 if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
321 return (-1);
323 DEBUG ("csv plugin: csv_write: filename = %s;", filename);
325 if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
326 return (-1);
328 if (use_stdio)
329 {
330 size_t i;
332 escape_string (filename, sizeof (filename));
334 /* Replace commas by colons for PUTVAL compatible output. */
335 for (i = 0; i < sizeof (values); i++)
336 {
337 if (values[i] == 0)
338 break;
339 else if (values[i] == ',')
340 values[i] = ':';
341 }
343 fprintf (use_stdio == 1 ? stdout : stderr,
344 "PUTVAL %s interval=%i %s\n",
345 filename, interval_g, values);
346 return (0);
347 }
349 if (stat (filename, &statbuf) == -1)
350 {
351 if (errno == ENOENT)
352 {
353 if (csv_create_file (filename, ds))
354 return (-1);
355 }
356 else
357 {
358 char errbuf[1024];
359 ERROR ("stat(%s) failed: %s", filename,
360 sstrerror (errno, errbuf,
361 sizeof (errbuf)));
362 return (-1);
363 }
364 }
365 else if (!S_ISREG (statbuf.st_mode))
366 {
367 ERROR ("stat(%s): Not a regular file!",
368 filename);
369 return (-1);
370 }
372 csv = fopen (filename, "a");
373 if (csv == NULL)
374 {
375 char errbuf[1024];
376 ERROR ("csv plugin: fopen (%s) failed: %s", filename,
377 sstrerror (errno, errbuf, sizeof (errbuf)));
378 return (-1);
379 }
380 csv_fd = fileno (csv);
382 memset (&fl, '\0', sizeof (fl));
383 fl.l_start = 0;
384 fl.l_len = 0; /* till end of file */
385 fl.l_pid = getpid ();
386 fl.l_type = F_WRLCK;
387 fl.l_whence = SEEK_SET;
389 status = fcntl (csv_fd, F_SETLK, &fl);
390 if (status != 0)
391 {
392 char errbuf[1024];
393 ERROR ("csv plugin: flock (%s) failed: %s", filename,
394 sstrerror (errno, errbuf, sizeof (errbuf)));
395 fclose (csv);
396 return (-1);
397 }
399 fprintf (csv, "%s\n", values);
401 /* The lock is implicitely released. I we don't release it explicitely
402 * because the `FILE *' may need to flush a cache first */
403 fclose (csv);
405 return (0);
406 } /* int csv_write */
408 void module_register (void)
409 {
410 plugin_register_config ("csv", csv_config,
411 config_keys, config_keys_num);
412 plugin_register_write ("csv", csv_write, /* user_data = */ NULL);
413 } /* void module_register */