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