Merge branch 'collectd-5.5' into collectd-5.6
[collectd.git] / src / utils_cmd_putval.c
1 /**
2  * collectd - src/utils_cmd_putval.c
3  * Copyright (C) 2007-2009  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "plugin.h"
31
32 #include "utils_parse_option.h"
33 #include "utils_cmd_putval.h"
34
35 #define print_to_socket(fh, ...) \
36     do { \
37         if (fprintf (fh, __VA_ARGS__) < 0) { \
38             char errbuf[1024]; \
39             WARNING ("handle_putval: failed to write to socket #%i: %s", \
40                     fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
41             sfree (vl.values); \
42             return -1; \
43         } \
44         fflush(fh); \
45     } while (0)
46
47 static int set_option (value_list_t *vl, const char *key, const char *value)
48 {
49         if ((vl == NULL) || (key == NULL) || (value == NULL))
50                 return (-1);
51
52         if (strcasecmp ("interval", key) == 0)
53         {
54                 double tmp;
55                 char *endptr;
56
57                 endptr = NULL;
58                 errno = 0;
59                 tmp = strtod (value, &endptr);
60
61                 if ((errno == 0) && (endptr != NULL)
62                                 && (endptr != value) && (tmp > 0.0))
63                         vl->interval = DOUBLE_TO_CDTIME_T (tmp);
64         }
65         else
66                 return (1);
67
68         return (0);
69 } /* int parse_option */
70
71 int handle_putval (FILE *fh, char *buffer)
72 {
73         char *command;
74         char *identifier;
75         char *hostname;
76         char *plugin;
77         char *plugin_instance;
78         char *type;
79         char *type_instance;
80         int   status;
81         int   values_submitted;
82
83         char *identifier_copy;
84
85         const data_set_t *ds;
86         value_list_t vl = VALUE_LIST_INIT;
87         vl.values = NULL;
88
89         DEBUG ("utils_cmd_putval: handle_putval (fh = %p, buffer = %s);",
90                         (void *) fh, buffer);
91
92         command = NULL;
93         status = parse_string (&buffer, &command);
94         if (status != 0)
95         {
96                 print_to_socket (fh, "-1 Cannot parse command.\n");
97                 return (-1);
98         }
99         assert (command != NULL);
100
101         if (strcasecmp ("PUTVAL", command) != 0)
102         {
103                 print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
104                 return (-1);
105         }
106
107         identifier = NULL;
108         status = parse_string (&buffer, &identifier);
109         if (status != 0)
110         {
111                 print_to_socket (fh, "-1 Cannot parse identifier.\n");
112                 return (-1);
113         }
114         assert (identifier != NULL);
115
116         /* parse_identifier() modifies its first argument,
117          * returning pointers into it */
118         identifier_copy = sstrdup (identifier);
119
120         status = parse_identifier (identifier_copy, &hostname,
121                         &plugin, &plugin_instance,
122                         &type, &type_instance);
123         if (status != 0)
124         {
125                 DEBUG ("handle_putval: Cannot parse identifier `%s'.",
126                                 identifier);
127                 print_to_socket (fh, "-1 Cannot parse identifier `%s'.\n",
128                                 identifier);
129                 sfree (identifier_copy);
130                 return (-1);
131         }
132
133         if ((strlen (hostname) >= sizeof (vl.host))
134                         || (strlen (plugin) >= sizeof (vl.plugin))
135                         || ((plugin_instance != NULL)
136                                 && (strlen (plugin_instance) >= sizeof (vl.plugin_instance)))
137                         || ((type_instance != NULL)
138                                 && (strlen (type_instance) >= sizeof (vl.type_instance))))
139         {
140                 print_to_socket (fh, "-1 Identifier too long.\n");
141                 sfree (identifier_copy);
142                 return (-1);
143         }
144
145         sstrncpy (vl.host, hostname, sizeof (vl.host));
146         sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
147         sstrncpy (vl.type, type, sizeof (vl.type));
148         if (plugin_instance != NULL)
149                 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
150         if (type_instance != NULL)
151                 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
152
153         ds = plugin_get_ds (type);
154         if (ds == NULL) {
155                 print_to_socket (fh, "-1 Type `%s' isn't defined.\n", type);
156                 sfree (identifier_copy);
157                 return (-1);
158         }
159
160         /* Free identifier_copy */
161         hostname = NULL;
162         plugin = NULL; plugin_instance = NULL;
163         type = NULL;   type_instance = NULL;
164         sfree (identifier_copy);
165
166         vl.values_len = ds->ds_num;
167         vl.values = malloc (vl.values_len * sizeof (*vl.values));
168         if (vl.values == NULL)
169         {
170                 print_to_socket (fh, "-1 malloc failed.\n");
171                 return (-1);
172         }
173
174         /* All the remaining fields are part of the optionlist. */
175         values_submitted = 0;
176         while (*buffer != 0)
177         {
178                 char *string = NULL;
179                 char *value  = NULL;
180
181                 status = parse_option (&buffer, &string, &value);
182                 if (status < 0)
183                 {
184                         /* parse_option failed, buffer has been modified.
185                          * => we need to abort */
186                         print_to_socket (fh, "-1 Misformatted option.\n");
187                         sfree (vl.values);
188                         return (-1);
189                 }
190                 else if (status == 0)
191                 {
192                         assert (string != NULL);
193                         assert (value != NULL);
194                         set_option (&vl, string, value);
195                         continue;
196                 }
197                 /* else: parse_option but buffer has not been modified. This is
198                  * the default if no `=' is found.. */
199
200                 status = parse_string (&buffer, &string);
201                 if (status != 0)
202                 {
203                         print_to_socket (fh, "-1 Misformatted value.\n");
204                         sfree (vl.values);
205                         return (-1);
206                 }
207                 assert (string != NULL);
208
209                 status = parse_values (string, &vl, ds);
210                 if (status != 0)
211                 {
212                         print_to_socket (fh, "-1 Parsing the values string failed.\n");
213                         sfree (vl.values);
214                         return (-1);
215                 }
216
217                 plugin_dispatch_values (&vl);
218                 values_submitted++;
219         } /* while (*buffer != 0) */
220         /* Done parsing the options. */
221
222         if (fh!=stdout)
223                 print_to_socket (fh, "0 Success: %i %s been dispatched.\n",
224                         values_submitted,
225                         (values_submitted == 1) ? "value has" : "values have");
226
227         sfree (vl.values);
228         return (0);
229 } /* int handle_putval */
230
231 int create_putval (char *ret, size_t ret_len, /* {{{ */
232         const data_set_t *ds, const value_list_t *vl)
233 {
234         char buffer_ident[6 * DATA_MAX_NAME_LEN];
235         char buffer_values[1024];
236         int status;
237
238         status = FORMAT_VL (buffer_ident, sizeof (buffer_ident), vl);
239         if (status != 0)
240                 return (status);
241         escape_string (buffer_ident, sizeof (buffer_ident));
242
243         status = format_values (buffer_values, sizeof (buffer_values),
244                         ds, vl, /* store rates = */ 0);
245         if (status != 0)
246                 return (status);
247         escape_string (buffer_values, sizeof (buffer_values));
248
249         ssnprintf (ret, ret_len,
250                         "PUTVAL %s interval=%.3f %s",
251                         buffer_ident,
252                         (vl->interval > 0)
253                         ? CDTIME_T_TO_DOUBLE (vl->interval)
254                         : CDTIME_T_TO_DOUBLE (plugin_get_interval ()),
255                         buffer_values);
256
257         return (0);
258 } /* }}} int create_putval */