2 * collectd - src/utils_cmds.c
3 * Copyright (C) 2008 Florian Forster
4 * Copyright (C) 2016 Sebastian 'tokkee' Harl
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
25 * Florian octo Forster <octo at collectd.org>
26 * Sebastian 'tokkee' Harl <sh at tokkee.org>
29 #include "utils_cmds.h"
30 #include "utils_cmd_flush.h"
31 #include "utils_cmd_putval.h"
32 #include "utils_parse_option.h"
33 #include "daemon/common.h"
39 * private helper functions
42 static cmd_status_t cmd_split (char *buffer,
43 size_t *ret_len, char ***ret_fields,
44 cmd_error_handler_t *err)
47 bool in_field, in_quotes;
54 for (string = buffer; *string != '\0'; ++string)
56 /* Make a quick worst-case estimate of the number of fields by
57 * counting spaces and ignoring quotation marks. */
58 if (!isspace ((int)*string))
72 /* fields will be NULL-terminated */
73 fields = malloc ((estimate + 1) * sizeof (*fields));
75 cmd_error (CMD_ERROR, err, "malloc failed.");
89 assert (len < estimate); \
90 fields[len] = field; \
99 for (string = buffer; *string != '\0'; string++)
101 if (isspace ((int)string[0]))
112 else if (string[0] == '"')
114 /* Note: Two consecutive quoted fields not separated by space are
115 * treated as different fields. This is the collectd 5.x behavior
116 * around splitting fields. */
120 /* end of quoted field */
121 if (! in_field) /* empty quoted string */
129 /* if (! in_field): add new field on next iteration
130 * else: quoted string following an unquoted string (one field)
131 * in either case: skip quotation mark */
134 else if ((string[0] == '\\') && in_quotes)
136 /* Outside of quotes, a backslash is a regular character (mostly
137 * for backward compatibility). */
139 if (string[1] == '\0')
142 cmd_error (CMD_PARSE_ERROR, err,
143 "Backslash at end of string.");
144 return (CMD_PARSE_ERROR);
147 /* un-escape the next character; skip backslash */
162 cmd_error (CMD_PARSE_ERROR, err, "Unterminated quoted string.");
163 return (CMD_PARSE_ERROR);
172 if (ret_fields != NULL)
173 *ret_fields = fields;
177 } /* int cmd_split */
183 void cmd_error (cmd_status_t status, cmd_error_handler_t *err,
184 const char *format, ...)
188 if ((err == NULL) || (err->cb == NULL))
191 va_start (ap, format);
192 err->cb (err->ud, status, format, ap);
194 } /* void cmd_error */
196 cmd_status_t cmd_parsev (size_t argc, char **argv,
197 cmd_t *ret_cmd, cmd_error_handler_t *err)
199 char *command = NULL;
201 if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL))
204 cmd_error (CMD_ERROR, err, "Missing command.");
208 memset (ret_cmd, 0, sizeof (*ret_cmd));
210 if (strcasecmp ("FLUSH", command) == 0)
212 ret_cmd->type = CMD_FLUSH;
213 return cmd_parse_flush (argc - 1, argv + 1,
214 &ret_cmd->cmd.flush, err);
216 else if (strcasecmp ("PUTVAL", command) == 0)
218 ret_cmd->type = CMD_PUTVAL;
219 return cmd_parse_putval (argc - 1, argv + 1,
220 &ret_cmd->cmd.putval, err);
224 ret_cmd->type = CMD_UNKNOWN;
225 cmd_error (CMD_UNKNOWN_COMMAND, err,
226 "Unknown command `%s'.", command);
227 return (CMD_UNKNOWN_COMMAND);
231 } /* cmd_status_t cmd_parsev */
233 cmd_status_t cmd_parse (char *buffer,
234 cmd_t *ret_cmd, cmd_error_handler_t *err)
236 char **fields = NULL;
237 size_t fields_num = 0;
240 if ((status = cmd_split (buffer, &fields_num, &fields, err)) != CMD_OK)
243 status = cmd_parsev (fields_num, fields, ret_cmd, err);
246 } /* cmd_status_t cmd_parse */
248 void cmd_destroy (cmd_t *cmd)
259 cmd_destroy_flush (&cmd->cmd.flush);
262 cmd_destroy_putval (&cmd->cmd.putval);
265 } /* void cmd_destroy */
267 cmd_status_t cmd_parse_option (char *field,
268 char **ret_key, char **ret_value, cmd_error_handler_t *err)
275 cmd_error (CMD_ERROR, err, "Invalid argument to cmd_parse_option.");
280 /* Look for the equal sign. */
281 while (isalnum ((int)value[0]) || (value[0] == '_') || (value[0] == ':'))
283 if ((value[0] != '=') || (value == key))
285 /* Whether this is a fatal error is up to the caller. */
286 return (CMD_NO_OPTION);
293 if (ret_value != NULL)
297 } /* cmd_status_t cmd_parse_option */
299 void cmd_error_fh (void *ud, cmd_status_t status,
300 const char *format, va_list ap)
306 if (status == CMD_OK)
309 vsnprintf (buf, sizeof(buf), format, ap);
310 buf[sizeof (buf) - 1] = '\0';
311 if (fprintf (fh, "%i %s\n", code, buf) < 0)
314 WARNING ("utils_cmds: failed to write to file-handle #%i: %s",
315 fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf)));
320 } /* void cmd_error_fh */
322 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */