blob: f113ba3033a58fdeaa48f9aded7bd1a822037a8c [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 Tarreau2ac57182012-04-19 15:24:50 +020044/* This function builds an argument list from a config line. It returns the
45 * number of arguments found, or <0 in case of any error. Everything needed
46 * it automatically allocated. A pointer to an error message might be returned
47 * in err_msg if not NULL, in which case it would be allocated and the caller
48 * will have to check it and free it. The output arg list is returned in argp
49 * which must be valid. The returned array is always terminated by an arg of
50 * type ARGT_STOP (0), unless the mask indicates that no argument is supported.
51 * The mask is composed of a number of mandatory arguments in its lower 4 bits,
52 * and a concatenation of each argument type in each subsequent 4-bit block. If
53 * <err_msg> is not NULL, it must point to a freeable or NULL pointer.
54 */
55int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
56 char **err_msg, const char **err_ptr, int *err_arg)
57{
58 int nbarg;
59 int pos;
60 struct arg *arg, *arg_list = NULL;
61 const char *beg;
62 char *word = NULL;
63 const char *ptr_err = NULL;
64 int min_arg;
65
66 min_arg = mask & 15;
67 mask >>= 4;
68
69 pos = 0;
70 /* find between 0 and 8 the max number of args supported by the mask */
71 for (nbarg = 0; nbarg < 8 && ((mask >> (nbarg * 4)) & 0xF); nbarg++);
72
73 if (!nbarg)
74 goto end_parse;
75
76 /* Note: an empty input string contains an empty argument if this argument
77 * is marked mandatory. Otherwise we can ignore it.
78 */
79 if (!len && !min_arg)
80 goto end_parse;
81
82 arg = arg_list = calloc(nbarg + 1, sizeof(*arg));
83
84 /* Note: empty arguments after a comma always exist. */
85 while (pos < nbarg) {
86 beg = in;
87 while (len && *in != ',') {
88 in++;
89 len--;
90 }
91
92 /* we have a new argument between <beg> and <in> (not included).
93 * For ease of handling, we copy it into a zero-terminated word.
94 * By default, the output argument will be the same type of the
95 * expected one.
96 */
97 free(word);
98 word = my_strndup(beg, in - beg);
99
100 arg->type = (mask >> (pos * 4)) & 15;
101
102 switch (arg->type) {
103 case ARGT_SINT:
104 if (in == beg) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200105 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200106 else if (*beg < '0' || *beg > '9') {
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200107 beg++;
108 arg->data.sint = read_uint(&beg, in);
109 if (beg < in)
110 goto parse_err;
111 if (*word == '-')
Willy Tarreau2ac57182012-04-19 15:24:50 +0200112 arg->data.sint = -arg->data.sint;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200113 else if (*word != '+') // invalid first character
Willy Tarreau2ac57182012-04-19 15:24:50 +0200114 goto parse_err;
115 break;
116 }
117
118 arg->type = ARGT_UINT;
119 /* fall through ARGT_UINT if no sign is present */
120
121 case ARGT_UINT:
122 if (in == beg) // empty number
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200123 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200124
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200125 arg->data.uint = read_uint(&beg, in);
126 if (beg < in)
127 goto parse_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200128 break;
129
130 case ARGT_FE:
131 case ARGT_BE:
132 case ARGT_TAB:
133 case ARGT_SRV:
134 case ARGT_USR:
Willy Tarreau496aa012012-06-01 10:38:29 +0200135 /* These argument types need to be stored as strings during
136 * parsing then resolved later.
137 */
138 arg->unresolved = 1;
139 /* fall through */
Willy Tarreau2ac57182012-04-19 15:24:50 +0200140 case ARGT_STR:
141 /* all types that must be resolved are stored as strings
142 * during the parsing. The caller must at one point resolve
143 * them and free the string.
144 */
145 arg->data.str.str = word;
146 arg->data.str.len = in - beg;
147 arg->data.str.size = arg->data.str.len + 1;
148 word = NULL;
149 break;
150
151 case ARGT_IPV4:
152 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200153 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200154
155 if (inet_pton(AF_INET, word, &arg->data.ipv4) <= 0)
156 goto parse_err;
157 break;
158
159 case ARGT_MSK4:
160 if (in == beg) // empty mask
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200161 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200162
163 if (!str2mask(word, &arg->data.ipv4))
164 goto parse_err;
165
166 arg->type = ARGT_IPV4;
167 break;
168
169 case ARGT_IPV6:
170 if (in == beg) // empty address
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200171 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200172
173 if (inet_pton(AF_INET6, word, &arg->data.ipv6) <= 0)
174 goto parse_err;
175 break;
176
177 case ARGT_MSK6: /* not yet implemented */
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200178 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200179
180 case ARGT_TIME:
181 if (in == beg) // empty time
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200182 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200183
184 ptr_err = parse_time_err(word, &arg->data.uint, TIME_UNIT_MS);
185 if (ptr_err)
186 goto parse_err;
187
188 arg->type = ARGT_UINT;
189 break;
190
191 case ARGT_SIZE:
192 if (in == beg) // empty size
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200193 goto empty_err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200194
195 ptr_err = parse_size_err(word, &arg->data.uint);
196 if (ptr_err)
197 goto parse_err;
198
199 arg->type = ARGT_UINT;
200 break;
201
202 /* FIXME: other types need to be implemented here */
203 default:
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200204 goto not_impl;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200205 }
206
207 pos++;
208 arg++;
209
210 /* don't go back to parsing if we reached end */
211 if (!len || pos >= nbarg)
212 break;
213
214 /* skip comma */
215 in++; len--;
216 }
217
218 end_parse:
219 free(word); word = NULL;
220
221 if (pos < min_arg) {
222 /* not enough arguments */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200223 memprintf(err_msg,
224 "Missing arguments (got %d/%d), type '%s' expected",
225 pos, min_arg, arg_type_names[(mask >> (pos * 4)) & 15]);
Willy Tarreau2ac57182012-04-19 15:24:50 +0200226 goto err;
227 }
228
229 if (len) {
230 /* too many arguments, starting at <in> */
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200231 /* the caller is responsible for freeing this message */
232 word = my_strndup(in, len);
233 memprintf(err_msg, "end of arguments expected at position %d, but got '%s'",
234 pos + 1, word);
235 free(word); word = NULL;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200236 goto err;
237 }
238
239 /* note that pos might be < nbarg and this is not an error, it's up to the
240 * caller to decide what to do with optional args.
241 */
242 *argp = arg_list;
243
244 if (err_arg)
245 *err_arg = pos;
246 if (err_ptr)
247 *err_ptr = in;
248 return pos;
249
Willy Tarreau2ac57182012-04-19 15:24:50 +0200250 err:
251 free(word);
252 free(arg_list);
253 if (err_arg)
254 *err_arg = pos;
255 if (err_ptr)
256 *err_ptr = in;
257 return -1;
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200258
259 empty_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200260 memprintf(err_msg, "expected type '%s' at position %d, but got nothing",
261 arg_type_names[(mask >> (pos * 4)) & 15], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200262 goto err;
263
264 parse_err:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200265 memprintf(err_msg, "failed to parse '%s' as type '%s' at position %d",
266 word, arg_type_names[(mask >> (pos * 4)) & 15], pos + 1);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200267 goto err;
268
269 not_impl:
Willy Tarreaueb6cead2012-09-20 19:43:14 +0200270 memprintf(err_msg, "parsing for type '%s' was not implemented, please report this bug",
271 arg_type_names[(mask >> (pos * 4)) & 15]);
Willy Tarreau4e6336f2012-04-27 16:32:26 +0200272 goto err;
Willy Tarreau2ac57182012-04-19 15:24:50 +0200273}