blob: 1bf6444d32b3bc1a665416095450e2375148d96b [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",
36 /* Unassigned types must never happen. Better crash during parsing if they do. */
37};
38
Willy Tarreau2e845be2012-10-19 19:49:09 +020039/* This dummy arg list may be used by default when no arg is found, it helps
40 * parsers by removing pointer checks.
41 */
42struct arg empty_arg_list[8] = { };
43
Willy Tarreaua4312fa2013-04-02 16:34:32 +020044/* This function clones a struct arg_list template into a new one which is
45 * returned.
46 */
47struct arg_list *arg_list_clone(const struct arg_list *orig)
48{
49 struct arg_list *new;
50
51 if ((new = calloc(1, sizeof(*new))) != NULL) {
52 /* ->list will be set by the caller when inserting the element.
53 * ->arg and ->arg_pos will be set by the caller.
54 */
55 new->ctx = orig->ctx;
56 new->kw = orig->kw;
57 new->conv = orig->conv;
58 new->file = orig->file;
59 new->line = orig->line;
60 }
61 return new;
62}
63
64/* This function clones a struct <arg_list> template into a new one which is
65 * set to point to arg <arg> at pos <pos>, and which is returned if the caller
66 * wants to apply further changes.
67 */
68struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos)
69{
70 struct arg_list *new;
71
72 new = arg_list_clone(orig);
73 new->arg = arg;
74 new->arg_pos = pos;
75 LIST_ADDQ(&orig->list, &new->list);
76 return new;
77}
78
Willy Tarreau2ac57182012-04-19 15:24:50 +020079/* This function builds an argument list from a config line. It returns the
80 * number of arguments found, or <0 in case of any error. Everything needed
81 * it automatically allocated. A pointer to an error message might be returned
82 * in err_msg if not NULL, in which case it would be allocated and the caller
83 * will have to check it and free it. The output arg list is returned in argp
84 * which must be valid. The returned array is always terminated by an arg of
85 * type ARGT_STOP (0), unless the mask indicates that no argument is supported.
Willy Tarreaua4312fa2013-04-02 16:34:32 +020086 * Unresolved arguments are appended to arg list <al>, which also serves as a
87 * template to create new entries. The mask is composed of a number of
88 * mandatory arguments in its lower 4 bits, and a concatenation of each
89 * argument type in each subsequent 4-bit block. If <err_msg> is not NULL, it
90 * must point to a freeable or NULL pointer.
Willy Tarreau2ac57182012-04-19 15:24:50 +020091 */
92int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
Willy Tarreaua4312fa2013-04-02 16:34:32 +020093 char **err_msg, const char **err_ptr, int *err_arg,
94 struct arg_list *al)
Willy Tarreau2ac57182012-04-19 15:24:50 +020095{
96 int nbarg;
97 int pos;
Willy Tarreaua4312fa2013-04-02 16:34:32 +020098 struct arg *arg;
Willy Tarreau2ac57182012-04-19 15:24:50 +020099 const char *beg;
100 char *word = NULL;
101 const char *ptr_err = NULL;
102 int min_arg;
103
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200104 *argp = NULL;
105
Willy Tarreau2ac57182012-04-19 15:24:50 +0200106 min_arg = mask & 15;
107 mask >>= 4;
108
109 pos = 0;
110 /* find between 0 and 8 the max number of args supported by the mask */
111 for (nbarg = 0; nbarg < 8 && ((mask >> (nbarg * 4)) & 0xF); nbarg++);
112
113 if (!nbarg)
114 goto end_parse;
115
116 /* Note: an empty input string contains an empty argument if this argument
117 * is marked mandatory. Otherwise we can ignore it.
118 */
119 if (!len && !min_arg)
120 goto end_parse;
121
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200122 arg = *argp = calloc(nbarg + 1, sizeof(*arg));
Willy Tarreau2ac57182012-04-19 15:24:50 +0200123
124 /* Note: empty arguments after a comma always exist. */
125 while (pos < nbarg) {
126 beg = in;
127 while (len && *in != ',') {
128 in++;
129 len--;
130 }
131
132 /* we have a new argument between <beg> and <in> (not included).
133 * For ease of handling, we copy it into a zero-terminated word.
134 * By default, the output argument will be the same type of the
135 * expected one.
136 */
137 free(word);
138 word = my_strndup(beg, in - beg);
139
140 arg->type = (mask >> (pos * 4)) & 15;
141
142 switch (arg->type) {
143 case ARGT_SINT:
144 if (in == beg) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200145 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200146 else if (*beg < '0' || *beg > '9') {
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200147 beg++;
148 arg->data.sint = read_uint(&beg, in);
149 if (beg < in)
150 goto parse_err;
151 if (*word == '-')
Willy Tarreau2ac57182012-04-19 15:24:50 +0200152 arg->data.sint = -arg->data.sint;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200153 else if (*word != '+') // invalid first character
Willy Tarreau2ac57182012-04-19 15:24:50 +0200154 goto parse_err;
155 break;
156 }
157
158 arg->type = ARGT_UINT;
159 /* fall through ARGT_UINT if no sign is present */
160
161 case ARGT_UINT:
162 if (in == beg) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200163 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200164
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200165 arg->data.uint = read_uint(&beg, in);
166 if (beg < in)
167 goto parse_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200168 break;
169
170 case ARGT_FE:
171 case ARGT_BE:
172 case ARGT_TAB:
173 case ARGT_SRV:
174 case ARGT_USR:
Willy Tarreau496aa012012-06-01 10:38:29 +0200175 /* These argument types need to be stored as strings during
176 * parsing then resolved later.
177 */
178 arg->unresolved = 1;
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200179 arg_list_add(al, arg, pos);
180
Willy Tarreau496aa012012-06-01 10:38:29 +0200181 /* fall through */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200182 case ARGT_STR:
183 /* all types that must be resolved are stored as strings
184 * during the parsing. The caller must at one point resolve
185 * them and free the string.
186 */
187 arg->data.str.str = word;
188 arg->data.str.len = in - beg;
189 arg->data.str.size = arg->data.str.len + 1;
190 word = NULL;
191 break;
192
193 case ARGT_IPV4:
194 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200195 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200196
197 if (inet_pton(AF_INET, word, &arg->data.ipv4) <= 0)
198 goto parse_err;
199 break;
200
201 case ARGT_MSK4:
202 if (in == beg) // empty mask
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200203 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200204
205 if (!str2mask(word, &arg->data.ipv4))
206 goto parse_err;
207
208 arg->type = ARGT_IPV4;
209 break;
210
211 case ARGT_IPV6:
212 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200213 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200214
215 if (inet_pton(AF_INET6, word, &arg->data.ipv6) <= 0)
216 goto parse_err;
217 break;
218
219 case ARGT_MSK6: /* not yet implemented */
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200220 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200221
222 case ARGT_TIME:
223 if (in == beg) // empty time
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200224 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200225
226 ptr_err = parse_time_err(word, &arg->data.uint, TIME_UNIT_MS);
227 if (ptr_err)
228 goto parse_err;
229
230 arg->type = ARGT_UINT;
231 break;
232
233 case ARGT_SIZE:
234 if (in == beg) // empty size
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200235 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200236
237 ptr_err = parse_size_err(word, &arg->data.uint);
238 if (ptr_err)
239 goto parse_err;
240
241 arg->type = ARGT_UINT;
242 break;
243
244 /* FIXME: other types need to be implemented here */
245 default:
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200246 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200247 }
248
249 pos++;
250 arg++;
251
252 /* don't go back to parsing if we reached end */
253 if (!len || pos >= nbarg)
254 break;
255
256 /* skip comma */
257 in++; len--;
258 }
259
260 end_parse:
261 free(word); word = NULL;
262
263 if (pos < min_arg) {
264 /* not enough arguments */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200265 memprintf(err_msg,
266 "Missing arguments (got %d/%d), type '%s' expected",
267 pos, min_arg, arg_type_names[(mask >> (pos * 4)) & 15]);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200268 goto err;
269 }
270
271 if (len) {
272 /* too many arguments, starting at <in> */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200273 /* the caller is responsible for freeing this message */
274 word = my_strndup(in, len);
275 memprintf(err_msg, "end of arguments expected at position %d, but got '%s'",
276 pos + 1, word);
277 free(word); word = NULL;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200278 goto err;
279 }
280
281 /* note that pos might be < nbarg and this is not an error, it's up to the
282 * caller to decide what to do with optional args.
283 */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200284 if (err_arg)
285 *err_arg = pos;
286 if (err_ptr)
287 *err_ptr = in;
288 return pos;
289
Willy Tarreau2ac57182012-04-19 15:24:50 +0200290 err:
291 free(word);
Willy Tarreaua4312fa2013-04-02 16:34:32 +0200292 free(*argp);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200293 if (err_arg)
294 *err_arg = pos;
295 if (err_ptr)
296 *err_ptr = in;
297 return -1;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200298
299 empty_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200300 memprintf(err_msg, "expected type '%s' at position %d, but got nothing",
301 arg_type_names[(mask >> (pos * 4)) & 15], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200302 goto err;
303
304 parse_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200305 memprintf(err_msg, "failed to parse '%s' as type '%s' at position %d",
306 word, arg_type_names[(mask >> (pos * 4)) & 15], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200307 goto err;
308
309 not_impl:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200310 memprintf(err_msg, "parsing for type '%s' was not implemented, please report this bug",
311 arg_type_names[(mask >> (pos * 4)) & 15]);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200312 goto err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200313}