blob: e609ac5f57a9fafd81f968d3ba83a2dcbf386a5e [file] [log] [blame]
Willy Tarreau2ac57182012-04-19 15:24:50 +02001/*
2 * Functions used to parse typed argument lists
3 *
4 * Copyright 2012 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <sys/types.h>
14#include <sys/socket.h>
15#include <arpa/inet.h>
16
17#include <common/standard.h>
18#include <proto/arg.h>
19
20static const char *arg_type_names[ARGT_NBTYPES] = {
21 [ARGT_STOP] = "end of arguments",
22 [ARGT_UINT] = "unsigned integer",
23 [ARGT_SINT] = "signed integer",
24 [ARGT_STR] = "string",
25 [ARGT_IPV4] = "IPv4 address",
26 [ARGT_MSK4] = "IPv4 mask",
27 [ARGT_IPV6] = "IPv6 address",
28 [ARGT_MSK6] = "IPv6 mask",
29 [ARGT_TIME] = "delay",
30 [ARGT_SIZE] = "size",
31 [ARGT_FE] = "frontend",
32 [ARGT_BE] = "backend",
33 [ARGT_TAB] = "table",
34 [ARGT_SRV] = "server",
35 [ARGT_USR] = "user list",
Willy Tarreau53c250e2015-01-19 18:58:20 +010036 [ARGT_MAP] = "map",
Willy Tarreau46947782015-01-19 19:00:58 +010037 [ARGT_REG] = "regex",
Willy Tarreau2ac57182012-04-19 15:24:50 +020038 /* Unassigned types must never happen. Better crash during parsing if they do. */
39};
40
Willy Tarreau2e845be2012-10-19 19:49:09 +020041/* This dummy arg list may be used by default when no arg is found, it helps
42 * parsers by removing pointer checks.
43 */
Willy Tarreau3d241e72015-01-19 18:44:07 +010044struct arg empty_arg_list[ARGM_NBARGS] = { };
Willy Tarreau2e845be2012-10-19 19:49:09 +020045
Willy Tarreaua4312fa2013-04-02 16:34:32 +020046/* This function clones a struct arg_list template into a new one which is
47 * returned.
48 */
49struct arg_list *arg_list_clone(const struct arg_list *orig)
50{
51 struct arg_list *new;
52
53 if ((new = calloc(1, sizeof(*new))) != NULL) {
54 /* ->list will be set by the caller when inserting the element.
55 * ->arg and ->arg_pos will be set by the caller.
56 */
57 new->ctx = orig->ctx;
58 new->kw = orig->kw;
59 new->conv = orig->conv;
60 new->file = orig->file;
61 new->line = orig->line;
62 }
63 return new;
64}
65
66/* This function clones a struct <arg_list> template into a new one which is
67 * set to point to arg <arg> at pos <pos>, and which is returned if the caller
68 * wants to apply further changes.
69 */
70struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos)
71{
72 struct arg_list *new;
73
74 new = arg_list_clone(orig);
75 new->arg = arg;
76 new->arg_pos = pos;
77 LIST_ADDQ(&orig->list, &new->list);
78 return new;
79}
80
Willy Tarreau2ac57182012-04-19 15:24:50 +020081/* This function builds an argument list from a config line. It returns the
82 * number of arguments found, or <0 in case of any error. Everything needed
83 * it automatically allocated. A pointer to an error message might be returned
84 * in err_msg if not NULL, in which case it would be allocated and the caller
85 * will have to check it and free it. The output arg list is returned in argp
86 * which must be valid. The returned array is always terminated by an arg of
87 * type ARGT_STOP (0), unless the mask indicates that no argument is supported.
Willy Tarreaua4312fa2013-04-02 16:34:32 +020088 * Unresolved arguments are appended to arg list <al>, which also serves as a
89 * template to create new entries. The mask is composed of a number of
Willy Tarreau3d241e72015-01-19 18:44:07 +010090 * mandatory arguments in its lower ARGM_BITS bits, and a concatenation of each
91 * argument type in each subsequent ARGT_BITS-bit sblock. If <err_msg> is not
92 * NULL, it must point to a freeable or NULL pointer.
Willy Tarreau2ac57182012-04-19 15:24:50 +020093 */
94int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
Willy Tarreaua4312fa2013-04-02 16:34:32 +020095 char **err_msg, const char **err_ptr, int *err_arg,
96 struct arg_list *al)
Willy Tarreau2ac57182012-04-19 15:24:50 +020097{
98 int nbarg;
99 int pos;
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200100 struct arg *arg;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200101 const char *beg;
102 char *word = NULL;
103 const char *ptr_err = NULL;
104 int min_arg;
105
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200106 *argp = NULL;
107
Willy Tarreau3d241e72015-01-19 18:44:07 +0100108 min_arg = mask & ARGM_MASK;
109 mask >>= ARGM_BITS;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200110
111 pos = 0;
Willy Tarreau3d241e72015-01-19 18:44:07 +0100112 /* find between 0 and NBARGS the max number of args supported by the mask */
113 for (nbarg = 0; nbarg < ARGM_NBARGS && ((mask >> (nbarg * ARGT_BITS)) & ARGT_MASK); nbarg++);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200114
115 if (!nbarg)
116 goto end_parse;
117
118 /* Note: an empty input string contains an empty argument if this argument
119 * is marked mandatory. Otherwise we can ignore it.
120 */
121 if (!len && !min_arg)
122 goto end_parse;
123
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200124 arg = *argp = calloc(nbarg + 1, sizeof(*arg));
Willy Tarreau2ac57182012-04-19 15:24:50 +0200125
126 /* Note: empty arguments after a comma always exist. */
127 while (pos < nbarg) {
128 beg = in;
129 while (len && *in != ',') {
130 in++;
131 len--;
132 }
133
134 /* we have a new argument between <beg> and <in> (not included).
135 * For ease of handling, we copy it into a zero-terminated word.
136 * By default, the output argument will be the same type of the
137 * expected one.
138 */
139 free(word);
140 word = my_strndup(beg, in - beg);
141
Willy Tarreau3d241e72015-01-19 18:44:07 +0100142 arg->type = (mask >> (pos * ARGT_BITS)) & ARGT_MASK;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200143
144 switch (arg->type) {
145 case ARGT_SINT:
146 if (in == beg) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200147 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200148 else if (*beg < '0' || *beg > '9') {
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200149 beg++;
150 arg->data.sint = read_uint(&beg, in);
151 if (beg < in)
152 goto parse_err;
153 if (*word == '-')
Willy Tarreau2ac57182012-04-19 15:24:50 +0200154 arg->data.sint = -arg->data.sint;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200155 else if (*word != '+') // invalid first character
Willy Tarreau2ac57182012-04-19 15:24:50 +0200156 goto parse_err;
157 break;
158 }
159
160 arg->type = ARGT_UINT;
161 /* fall through ARGT_UINT if no sign is present */
162
163 case ARGT_UINT:
164 if (in == beg) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200165 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200166
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200167 arg->data.uint = read_uint(&beg, in);
168 if (beg < in)
169 goto parse_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200170 break;
171
172 case ARGT_FE:
173 case ARGT_BE:
174 case ARGT_TAB:
175 case ARGT_SRV:
176 case ARGT_USR:
Willy Tarreau46947782015-01-19 19:00:58 +0100177 case ARGT_REG:
Willy Tarreau496aa012012-06-01 10:38:29 +0200178 /* These argument types need to be stored as strings during
179 * parsing then resolved later.
180 */
181 arg->unresolved = 1;
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200182 arg_list_add(al, arg, pos);
183
Willy Tarreau496aa012012-06-01 10:38:29 +0200184 /* fall through */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200185 case ARGT_STR:
186 /* all types that must be resolved are stored as strings
187 * during the parsing. The caller must at one point resolve
188 * them and free the string.
189 */
190 arg->data.str.str = word;
191 arg->data.str.len = in - beg;
192 arg->data.str.size = arg->data.str.len + 1;
193 word = NULL;
194 break;
195
196 case ARGT_IPV4:
197 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200198 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200199
200 if (inet_pton(AF_INET, word, &arg->data.ipv4) <= 0)
201 goto parse_err;
202 break;
203
204 case ARGT_MSK4:
205 if (in == beg) // empty mask
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200206 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200207
208 if (!str2mask(word, &arg->data.ipv4))
209 goto parse_err;
210
211 arg->type = ARGT_IPV4;
212 break;
213
214 case ARGT_IPV6:
215 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200216 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200217
218 if (inet_pton(AF_INET6, word, &arg->data.ipv6) <= 0)
219 goto parse_err;
220 break;
221
222 case ARGT_MSK6: /* not yet implemented */
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200223 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200224
225 case ARGT_TIME:
226 if (in == beg) // empty time
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200227 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200228
229 ptr_err = parse_time_err(word, &arg->data.uint, TIME_UNIT_MS);
230 if (ptr_err)
231 goto parse_err;
232
233 arg->type = ARGT_UINT;
234 break;
235
236 case ARGT_SIZE:
237 if (in == beg) // empty size
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200238 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200239
240 ptr_err = parse_size_err(word, &arg->data.uint);
241 if (ptr_err)
242 goto parse_err;
243
244 arg->type = ARGT_UINT;
245 break;
246
247 /* FIXME: other types need to be implemented here */
248 default:
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200249 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200250 }
251
252 pos++;
253 arg++;
254
255 /* don't go back to parsing if we reached end */
256 if (!len || pos >= nbarg)
257 break;
258
259 /* skip comma */
260 in++; len--;
261 }
262
263 end_parse:
264 free(word); word = NULL;
265
266 if (pos < min_arg) {
267 /* not enough arguments */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200268 memprintf(err_msg,
Willy Tarreaub111d422013-12-13 00:38:47 +0100269 "missing arguments (got %d/%d), type '%s' expected",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100270 pos, min_arg, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200271 goto err;
272 }
273
274 if (len) {
275 /* too many arguments, starting at <in> */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200276 /* the caller is responsible for freeing this message */
277 word = my_strndup(in, len);
Willy Tarreaub111d422013-12-13 00:38:47 +0100278 if (nbarg)
279 memprintf(err_msg, "end of arguments expected at position %d, but got '%s'",
280 pos + 1, word);
281 else
282 memprintf(err_msg, "no argument supported, but got '%s'", word);
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200283 free(word); word = NULL;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200284 goto err;
285 }
286
287 /* note that pos might be < nbarg and this is not an error, it's up to the
288 * caller to decide what to do with optional args.
289 */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200290 if (err_arg)
291 *err_arg = pos;
292 if (err_ptr)
293 *err_ptr = in;
294 return pos;
295
Willy Tarreau2ac57182012-04-19 15:24:50 +0200296 err:
297 free(word);
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200298 free(*argp);
Willy Tarreau681e49d2013-12-06 15:30:05 +0100299 *argp = NULL;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200300 if (err_arg)
301 *err_arg = pos;
302 if (err_ptr)
303 *err_ptr = in;
304 return -1;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200305
306 empty_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200307 memprintf(err_msg, "expected type '%s' at position %d, but got nothing",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100308 arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200309 goto err;
310
311 parse_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200312 memprintf(err_msg, "failed to parse '%s' as type '%s' at position %d",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100313 word, arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200314 goto err;
315
316 not_impl:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200317 memprintf(err_msg, "parsing for type '%s' was not implemented, please report this bug",
Willy Tarreau3d241e72015-01-19 18:44:07 +0100318 arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK]);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200319 goto err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200320}