b5333b436faa1ceb236d1b114e6905875b9316b2
[collectd.git] / src / csv.c
1 /**
2  * collectd - src/csv.c
3  * Copyright (C) 2007  Florian octo Forster
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  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "plugin.h"
24 #include "common.h"
25 #include "utils_cache.h"
26
27 /*
28  * Private variables
29  */
30 static const char *config_keys[] =
31 {
32         "DataDir",
33         "StoreRates"
34 };
35 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
36
37 static char *datadir   = NULL;
38 static int store_rates = 0;
39 static int use_stdio   = 0;
40
41 static int value_list_to_string (char *buffer, int buffer_len,
42                 const data_set_t *ds, const value_list_t *vl)
43 {
44         int offset;
45         int status;
46         int i;
47         gauge_t *rates = NULL;
48
49         assert (0 == strcmp (ds->type, vl->type));
50
51         memset (buffer, '\0', buffer_len);
52
53         status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
54         if ((status < 1) || (status >= buffer_len))
55                 return (-1);
56         offset = status;
57
58         for (i = 0; i < ds->ds_num; i++)
59         {
60                 if ((ds->ds[i].type != DS_TYPE_COUNTER)
61                                 && (ds->ds[i].type != DS_TYPE_GAUGE))
62                         return (-1);
63
64                 if (ds->ds[i].type == DS_TYPE_COUNTER)
65                 {
66                         if (store_rates == 0)
67                         {
68                                 status = ssnprintf (buffer + offset,
69                                                 buffer_len - offset,
70                                                 ",%llu",
71                                                 vl->values[i].counter);
72                         }
73                         else /* if (store_rates == 1) */
74                         {
75                                 if (rates == NULL)
76                                         rates = uc_get_rate (ds, vl);
77                                 if (rates == NULL)
78                                 {
79                                         WARNING ("csv plugin: "
80                                                         "uc_get_rate failed.");
81                                         return (-1);
82                                 }
83                                 status = ssnprintf (buffer + offset,
84                                                 buffer_len - offset,
85                                                 ",%lf", rates[i]);
86                         }
87                 }
88                 else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
89                 {
90                         status = ssnprintf (buffer + offset, buffer_len - offset,
91                                         ",%lf", vl->values[i].gauge);
92                 }
93
94                 if ((status < 1) || (status >= (buffer_len - offset)))
95                 {
96                         sfree (rates);
97                         return (-1);
98                 }
99
100                 offset += status;
101         } /* for ds->ds_num */
102
103         sfree (rates);
104         return (0);
105 } /* int value_list_to_string */
106
107 static int value_list_to_filename (char *buffer, int buffer_len,
108                 const data_set_t *ds, const value_list_t *vl)
109 {
110         int offset = 0;
111         int status;
112
113         assert (0 == strcmp (ds->type, vl->type));
114
115         if (datadir != NULL)
116         {
117                 status = ssnprintf (buffer + offset, buffer_len - offset,
118                                 "%s/", datadir);
119                 if ((status < 1) || (status >= buffer_len - offset))
120                         return (-1);
121                 offset += status;
122         }
123
124         status = ssnprintf (buffer + offset, buffer_len - offset,
125                         "%s/", vl->host);
126         if ((status < 1) || (status >= buffer_len - offset))
127                 return (-1);
128         offset += status;
129
130         if (strlen (vl->plugin_instance) > 0)
131                 status = ssnprintf (buffer + offset, buffer_len - offset,
132                                 "%s-%s/", vl->plugin, vl->plugin_instance);
133         else
134                 status = ssnprintf (buffer + offset, buffer_len - offset,
135                                 "%s/", vl->plugin);
136         if ((status < 1) || (status >= buffer_len - offset))
137                 return (-1);
138         offset += status;
139
140         if (strlen (vl->type_instance) > 0)
141                 status = ssnprintf (buffer + offset, buffer_len - offset,
142                                 "%s-%s", vl->type, vl->type_instance);
143         else
144                 status = ssnprintf (buffer + offset, buffer_len - offset,
145                                 "%s", vl->type);
146         if ((status < 1) || (status >= buffer_len - offset))
147                 return (-1);
148         offset += status;
149
150         if (!use_stdio)
151         {
152                 time_t now;
153                 struct tm stm;
154
155                 /* TODO: Find a way to minimize the calls to `localtime_r',
156                  * since they are pretty expensive.. */
157                 now = time (NULL);
158                 if (localtime_r (&now, &stm) == NULL)
159                 {
160                         ERROR ("csv plugin: localtime_r failed");
161                         return (1);
162                 }
163
164                 strftime (buffer + offset, buffer_len - offset,
165                                 "-%Y-%m-%d", &stm);
166         }
167
168         return (0);
169 } /* int value_list_to_filename */
170
171 static int csv_create_file (const char *filename, const data_set_t *ds)
172 {
173         FILE *csv;
174         int i;
175
176         if (check_create_dir (filename))
177                 return (-1);
178
179         csv = fopen (filename, "w");
180         if (csv == NULL)
181         {
182                 char errbuf[1024];
183                 ERROR ("csv plugin: fopen (%s) failed: %s",
184                                 filename,
185                                 sstrerror (errno, errbuf, sizeof (errbuf)));
186                 return (-1);
187         }
188
189         fprintf (csv, "epoch");
190         for (i = 0; i < ds->ds_num; i++)
191                 fprintf (csv, ",%s", ds->ds[i].name);
192
193         fprintf (csv, "\n");
194         fclose (csv);
195
196         return 0;
197 } /* int csv_create_file */
198
199 static int csv_config (const char *key, const char *value)
200 {
201         if (strcasecmp ("DataDir", key) == 0)
202         {
203                 if (datadir != NULL)
204                         free (datadir);
205                 if (strcasecmp ("stdout", value) == 0)
206                 {
207                         use_stdio = 1;
208                         return (0);
209                 }
210                 else if (strcasecmp ("stderr", value) == 0)
211                 {
212                         use_stdio = 2;
213                         return (0);
214                 }
215                 datadir = strdup (value);
216                 if (datadir != NULL)
217                 {
218                         int len = strlen (datadir);
219                         while ((len > 0) && (datadir[len - 1] == '/'))
220                         {
221                                 len--;
222                                 datadir[len] = '\0';
223                         }
224                         if (len <= 0)
225                         {
226                                 free (datadir);
227                                 datadir = NULL;
228                         }
229                 }
230         }
231         else if (strcasecmp ("StoreRates", key) == 0)
232         {
233                 if ((strcasecmp ("True", value) == 0)
234                                 || (strcasecmp ("Yes", value) == 0)
235                                 || (strcasecmp ("On", value) == 0))
236                 {
237                         store_rates = 1;
238                 }
239                 else
240                 {
241                         store_rates = 0;
242                 }
243         }
244         else
245         {
246                 return (-1);
247         }
248         return (0);
249 } /* int csv_config */
250
251 static int csv_write (const data_set_t *ds, const value_list_t *vl)
252 {
253         struct stat  statbuf;
254         char         filename[512];
255         char         values[512];
256         FILE        *csv;
257         int          csv_fd;
258         struct flock fl;
259         int          status;
260
261         if (0 != strcmp (ds->type, vl->type)) {
262                 ERROR ("csv plugin: DS type does not match value list type");
263                 return -1;
264         }
265
266         if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
267                 return (-1);
268
269         DEBUG ("csv plugin: csv_write: filename = %s;", filename);
270
271         if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
272                 return (-1);
273
274         if (use_stdio)
275         {
276                 fprintf (use_stdio == 1 ? stdout : stderr,
277                          "%s=%s\n", filename, values);
278                 return (0);
279         }
280
281         if (stat (filename, &statbuf) == -1)
282         {
283                 if (errno == ENOENT)
284                 {
285                         if (csv_create_file (filename, ds))
286                                 return (-1);
287                 }
288                 else
289                 {
290                         char errbuf[1024];
291                         ERROR ("stat(%s) failed: %s", filename,
292                                         sstrerror (errno, errbuf,
293                                                 sizeof (errbuf)));
294                         return (-1);
295                 }
296         }
297         else if (!S_ISREG (statbuf.st_mode))
298         {
299                 ERROR ("stat(%s): Not a regular file!",
300                                 filename);
301                 return (-1);
302         }
303
304         csv = fopen (filename, "a");
305         if (csv == NULL)
306         {
307                 char errbuf[1024];
308                 ERROR ("csv plugin: fopen (%s) failed: %s", filename,
309                                 sstrerror (errno, errbuf, sizeof (errbuf)));
310                 return (-1);
311         }
312         csv_fd = fileno (csv);
313
314         memset (&fl, '\0', sizeof (fl));
315         fl.l_start  = 0;
316         fl.l_len    = 0; /* till end of file */
317         fl.l_pid    = getpid ();
318         fl.l_type   = F_WRLCK;
319         fl.l_whence = SEEK_SET;
320
321         status = fcntl (csv_fd, F_SETLK, &fl);
322         if (status != 0)
323         {
324                 char errbuf[1024];
325                 ERROR ("csv plugin: flock (%s) failed: %s", filename,
326                                 sstrerror (errno, errbuf, sizeof (errbuf)));
327                 fclose (csv);
328                 return (-1);
329         }
330
331         fprintf (csv, "%s\n", values);
332
333         /* The lock is implicitely released. I we don't release it explicitely
334          * because the `FILE *' may need to flush a cache first */
335         fclose (csv);
336
337         return (0);
338 } /* int csv_write */
339
340 void module_register (void)
341 {
342         plugin_register_config ("csv", csv_config,
343                         config_keys, config_keys_num);
344         plugin_register_write ("csv", csv_write);
345 } /* void module_register */