Line | Count | Source |
1 | | #include <ctype.h> |
2 | | |
3 | | #include <haproxy/api.h> |
4 | | #include <haproxy/arg.h> |
5 | | #include <haproxy/buf.h> |
6 | | #include <haproxy/cfgparse.h> |
7 | | #include <haproxy/check.h> |
8 | | #include <haproxy/cli.h> |
9 | | #include <haproxy/global.h> |
10 | | #include <haproxy/http.h> |
11 | | #include <haproxy/http_rules.h> |
12 | | #include <haproxy/intops.h> |
13 | | #include <haproxy/list.h> |
14 | | #include <haproxy/pattern.h> |
15 | | #include <haproxy/log.h> |
16 | | #include <haproxy/sample.h> |
17 | | #include <haproxy/session.h> |
18 | | #include <haproxy/stream-t.h> |
19 | | #include <haproxy/tcp_rules.h> |
20 | | #include <haproxy/tcpcheck.h> |
21 | | #include <haproxy/tools.h> |
22 | | #include <haproxy/vars.h> |
23 | | #include <haproxy/xxhash.h> |
24 | | |
25 | | #include <import/ceb64_tree.h> |
26 | | |
27 | | |
28 | | /* This contains a pool of struct vars */ |
29 | | DECLARE_STATIC_TYPED_POOL(var_pool, "vars", struct var); |
30 | | |
31 | | /* list of variables for the process scope. */ |
32 | | struct vars proc_vars THREAD_ALIGNED(); |
33 | | |
34 | | /* This array of int contains the system limits per context. */ |
35 | | static unsigned int var_global_limit = 0; |
36 | | static unsigned int var_proc_limit = 0; |
37 | | static unsigned int var_sess_limit = 0; |
38 | | static unsigned int var_txn_limit = 0; |
39 | | static unsigned int var_reqres_limit = 0; |
40 | | static unsigned int var_check_limit = 0; |
41 | | static uint64_t var_name_hash_seed = 0; |
42 | | static unsigned int svfm_max = 100; /* Max number of entries processed in set-vars-from-map */ |
43 | | |
44 | | /* Structure and array matching set-var conditions to their respective flag |
45 | | * value. |
46 | | */ |
47 | | struct var_set_condition { |
48 | | const char *cond_str; |
49 | | uint flag; |
50 | | }; |
51 | | |
52 | | static struct var_set_condition conditions_array[] = { |
53 | | { "ifexists", VF_COND_IFEXISTS }, |
54 | | { "ifnotexists", VF_COND_IFNOTEXISTS }, |
55 | | { "ifempty", VF_COND_IFEMPTY }, |
56 | | { "ifnotempty", VF_COND_IFNOTEMPTY }, |
57 | | { "ifset", VF_COND_IFSET }, |
58 | | { "ifnotset", VF_COND_IFNOTSET }, |
59 | | { "ifgt", VF_COND_IFGT }, |
60 | | { "iflt", VF_COND_IFLT }, |
61 | | { NULL, 0 } |
62 | | }; |
63 | | |
64 | | /* Variable scope names with their prefixes for output */ |
65 | | static const char *var_scope_names[] = { |
66 | | [SCOPE_SESS] = "sess.", |
67 | | [SCOPE_TXN] = "txn.", |
68 | | [SCOPE_REQ] = "req.", |
69 | | [SCOPE_RES] = "res.", |
70 | | [SCOPE_PROC] = "proc.", |
71 | | [SCOPE_CHECK] = "check.", |
72 | | }; |
73 | | |
74 | | /* returns the struct vars pointer for a session, stream and scope, or NULL if |
75 | | * it does not exist. |
76 | | */ |
77 | | static inline struct vars *get_vars(struct session *sess, struct stream *strm, const struct var_desc *desc) |
78 | 0 | { |
79 | 0 | if (!desc) |
80 | 0 | return NULL; |
81 | | |
82 | 0 | if (desc->flags & VDF_PARENT_CTX) { |
83 | 0 | if (!strm || !strm->parent) |
84 | 0 | return NULL; |
85 | 0 | strm = strm->parent; |
86 | 0 | sess = strm_sess(strm); |
87 | 0 | } |
88 | | |
89 | 0 | switch (desc->scope) { |
90 | 0 | case SCOPE_PROC: |
91 | 0 | return &proc_vars; |
92 | 0 | case SCOPE_SESS: |
93 | 0 | return sess ? &sess->vars : NULL; |
94 | 0 | case SCOPE_CHECK: { |
95 | 0 | struct check *check = sess ? objt_check(sess->origin) : NULL; |
96 | |
|
97 | 0 | return check ? &check->vars : NULL; |
98 | 0 | } |
99 | 0 | case SCOPE_TXN: |
100 | 0 | return strm ? &strm->vars_txn : NULL; |
101 | 0 | case SCOPE_REQ: |
102 | 0 | case SCOPE_RES: |
103 | 0 | default: |
104 | 0 | return strm ? &strm->vars_reqres : NULL; |
105 | 0 | } |
106 | 0 | } |
107 | | |
108 | | /* This function adds or remove memory size from the accounting. The inner |
109 | | * pointers may be null when setting the outer ones only. |
110 | | */ |
111 | | void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size) |
112 | 0 | { |
113 | 0 | switch (vars->scope) { |
114 | 0 | case SCOPE_REQ: |
115 | 0 | case SCOPE_RES: |
116 | 0 | if (var_reqres_limit && strm) |
117 | 0 | _HA_ATOMIC_ADD(&strm->vars_reqres.size, size); |
118 | 0 | __fallthrough; |
119 | 0 | case SCOPE_TXN: |
120 | 0 | if (var_txn_limit && strm) |
121 | 0 | _HA_ATOMIC_ADD(&strm->vars_txn.size, size); |
122 | 0 | goto scope_sess; |
123 | 0 | case SCOPE_CHECK: |
124 | 0 | if (var_check_limit) { |
125 | 0 | struct check *check = objt_check(sess->origin); |
126 | |
|
127 | 0 | if (check) |
128 | 0 | _HA_ATOMIC_ADD(&check->vars.size, size); |
129 | 0 | } |
130 | 0 | scope_sess: |
131 | 0 | __fallthrough; |
132 | 0 | case SCOPE_SESS: |
133 | 0 | if (var_sess_limit) |
134 | 0 | _HA_ATOMIC_ADD(&sess->vars.size, size); |
135 | 0 | __fallthrough; |
136 | 0 | case SCOPE_PROC: |
137 | 0 | if (var_proc_limit || var_global_limit) |
138 | 0 | _HA_ATOMIC_ADD(&proc_vars.size, size); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | /* This function returns 1 if the <size> is available in the var |
143 | | * pool <vars>, otherwise returns 0. If the space is available, |
144 | | * the size is reserved. The inner pointers may be null when setting |
145 | | * the outer ones only. The accounting uses either <sess> or <strm> |
146 | | * depending on the scope. <strm> may be NULL when no stream is known |
147 | | * and only the session exists (eg: tcp-request connection). |
148 | | */ |
149 | | static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size) |
150 | 0 | { |
151 | 0 | switch (vars->scope) { |
152 | 0 | case SCOPE_REQ: |
153 | 0 | case SCOPE_RES: |
154 | 0 | if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit) |
155 | 0 | return 0; |
156 | 0 | __fallthrough; |
157 | 0 | case SCOPE_TXN: |
158 | 0 | if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit) |
159 | 0 | return 0; |
160 | 0 | goto scope_sess; |
161 | 0 | case SCOPE_CHECK: { |
162 | 0 | struct check *check = objt_check(sess->origin); |
163 | |
|
164 | 0 | if (var_check_limit && check && check->vars.size + size > var_check_limit) |
165 | 0 | return 0; |
166 | 0 | } |
167 | 0 | scope_sess: |
168 | 0 | __fallthrough; |
169 | 0 | case SCOPE_SESS: |
170 | 0 | if (var_sess_limit && sess->vars.size + size > var_sess_limit) |
171 | 0 | return 0; |
172 | 0 | __fallthrough; |
173 | 0 | case SCOPE_PROC: |
174 | | /* note: scope proc collects all others and is currently identical to the |
175 | | * global limit. |
176 | | */ |
177 | 0 | if (var_proc_limit && proc_vars.size + size > var_proc_limit) |
178 | 0 | return 0; |
179 | 0 | if (var_global_limit && proc_vars.size + size > var_global_limit) |
180 | 0 | return 0; |
181 | 0 | } |
182 | 0 | var_accounting_diff(vars, sess, strm, size); |
183 | 0 | return 1; |
184 | 0 | } |
185 | | |
186 | | /* |
187 | | * Parse the scope part of a variable name starting at <scope_name> and compare |
188 | | * it to the "regular" scope names (see var_scope_names array). The scope part |
189 | | * ends at the first '.' or at the end of the string. If provided, the <end> |
190 | | * pointer is set past the scope, i.e. after the '.' if there is one, or on the |
191 | | * terminating nul byte otherwise. |
192 | | * Returns -1 in case of error, a vars_scope value otherwise. |
193 | | */ |
194 | | static int var_parse_scope_str(const char *scope_name, const char **end) |
195 | 0 | { |
196 | 0 | const char *dot; |
197 | 0 | size_t len; |
198 | | |
199 | | /* The scope ends at the first '.' or at the end of the string. */ |
200 | 0 | for (dot = scope_name; *dot && *dot != '.'; dot++) |
201 | 0 | ; |
202 | |
|
203 | 0 | len = dot - scope_name; |
204 | |
|
205 | 0 | if (end) |
206 | 0 | *end = *dot ? dot + 1 : dot; |
207 | |
|
208 | 0 | if (len == 3 && strncmp(scope_name, "req", 3) == 0) |
209 | 0 | return SCOPE_REQ; |
210 | 0 | if (len == 3 && strncmp(scope_name, "res", 3) == 0) |
211 | 0 | return SCOPE_RES; |
212 | 0 | if (len == 3 && strncmp(scope_name, "txn", 3) == 0) |
213 | 0 | return SCOPE_TXN; |
214 | 0 | if (len == 4 && strncmp(scope_name, "proc", 4) == 0) |
215 | 0 | return SCOPE_PROC; |
216 | 0 | if (len == 4 && strncmp(scope_name, "sess", 4) == 0) |
217 | 0 | return SCOPE_SESS; |
218 | 0 | if (len == 5 && strncmp(scope_name, "check", 5) == 0) |
219 | 0 | return SCOPE_CHECK; |
220 | | |
221 | 0 | return -1; |
222 | 0 | } |
223 | | |
224 | | /* This function removes a variable from the list and frees the memory it was |
225 | | * using. If the variable is marked "VF_PERMANENT", the sample_data is only |
226 | | * reset to SMP_T_ANY unless <force> is non nul. Returns the freed size. |
227 | | */ |
228 | | unsigned int var_clear(struct vars *vars, struct var *var, int force) |
229 | 0 | { |
230 | 0 | unsigned int size = 0; |
231 | |
|
232 | 0 | ha_free(&var->name); |
233 | |
|
234 | 0 | if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) { |
235 | 0 | ha_free(&var->data.u.str.area); |
236 | 0 | size += var->data.u.str.data; |
237 | 0 | } |
238 | 0 | else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) { |
239 | 0 | ha_free(&var->data.u.meth.str.area); |
240 | 0 | size += var->data.u.meth.str.data; |
241 | 0 | } |
242 | | /* wipe the sample */ |
243 | 0 | var->data.type = SMP_T_ANY; |
244 | |
|
245 | 0 | if (!(var->flags & VF_PERMANENT) || force) { |
246 | 0 | cebu64_item_delete(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], |
247 | 0 | name_node, name_hash, var); |
248 | 0 | pool_free(var_pool, var); |
249 | 0 | size += sizeof(struct var); |
250 | 0 | } |
251 | 0 | return size; |
252 | 0 | } |
253 | | |
254 | | /* This function frees all the memory used by all the session variables in the |
255 | | * list starting at <vars>. |
256 | | */ |
257 | | void vars_prune_per_sess(struct vars *vars) |
258 | 0 | { |
259 | 0 | struct var *var; |
260 | 0 | unsigned int size = 0; |
261 | 0 | int i; |
262 | |
|
263 | 0 | for (i = 0; i < VAR_NAME_ROOTS; i++) { |
264 | 0 | while ((var = cebu64_item_first(&vars->name_root[i], name_node, name_hash, struct var))) |
265 | 0 | size += var_clear(vars, var, 1); |
266 | 0 | } |
267 | |
|
268 | 0 | if (!size) |
269 | 0 | return; |
270 | | |
271 | 0 | if (var_sess_limit) |
272 | 0 | _HA_ATOMIC_SUB(&vars->size, size); |
273 | 0 | if (var_proc_limit || var_global_limit) |
274 | 0 | _HA_ATOMIC_SUB(&proc_vars.size, size); |
275 | 0 | } |
276 | | |
277 | | /* This function initializes a variables list head */ |
278 | | void vars_init_head(struct vars *vars, enum vars_scope scope) |
279 | 0 | { |
280 | 0 | memset(vars->name_root, 0, sizeof(vars->name_root)); |
281 | 0 | vars->scope = scope; |
282 | 0 | vars->size = 0; |
283 | 0 | HA_RWLOCK_INIT(&vars->rwlock); |
284 | 0 | } |
285 | | |
286 | | /* Returns non-zero if <name>, of length <len>, is a valid variable name part, |
287 | | * i.e. the part following the "<scope>." prefix. A valid part must not be |
288 | | * empty and may only contain alphanumerics, '_' and '.'. When <off> is not |
289 | | * NULL, it is set to the offset of the first invalid character, or to <len> |
290 | | * when the whole name is valid, so that callers may report it. |
291 | | */ |
292 | | static int var_name_is_valid(const char *name, size_t len, size_t *off) |
293 | 0 | { |
294 | 0 | size_t i; |
295 | |
|
296 | 0 | for (i = 0; i < len; i++) { |
297 | 0 | if (!isalnum((unsigned char)name[i]) && name[i] != '_' && name[i] != '.') { |
298 | 0 | if (off) |
299 | 0 | *off = i; |
300 | 0 | return 0; |
301 | 0 | } |
302 | 0 | } |
303 | | |
304 | 0 | if (off) |
305 | 0 | *off = len; |
306 | 0 | return len != 0; |
307 | 0 | } |
308 | | |
309 | | /* This function returns the description (a var_desc structure) of a variable |
310 | | * name of a specified length. It makes sure that the scope is valid. It fills |
311 | | * <desc> passed as parameter and returns non-zero on success, 0 on |
312 | | * failure. Neither hash nor scope may be NULL. |
313 | | */ |
314 | | static int vars_fill_desc(const char *name, int len, struct var_desc *desc, char **err) |
315 | 0 | { |
316 | 0 | const char *endptr = NULL; |
317 | 0 | size_t off; |
318 | | |
319 | | /* Check name and length. */ |
320 | 0 | if (name == NULL || len == 0) { |
321 | 0 | memprintf(err, "Empty variable name cannot be accepted"); |
322 | 0 | return 0; |
323 | 0 | } |
324 | | /* Check desc */ |
325 | 0 | if (desc == NULL) { |
326 | 0 | memprintf(err, "Invalid variable description"); |
327 | 0 | return 0; |
328 | 0 | } |
329 | | |
330 | 0 | desc->flags = 0; |
331 | | |
332 | | /* Check scope including those related to the parent stream (prefixed by ('p'). */ |
333 | 0 | if ((desc->scope = var_parse_scope_str(name, &endptr)) != -1) { |
334 | | /* The scope must be followed by a '.' and a non-empty name */ |
335 | 0 | if (!endptr || !*endptr) |
336 | 0 | goto err; |
337 | 0 | len -= (endptr-name); |
338 | 0 | name = endptr; |
339 | 0 | if (len <= 0) |
340 | 0 | goto err; |
341 | 0 | } else if (len > 6 && strncmp(name, "psess.", 6) == 0) { |
342 | 0 | name += 6; |
343 | 0 | len -= 6; |
344 | 0 | desc->scope = SCOPE_SESS; |
345 | 0 | desc->flags |= VDF_PARENT_CTX; |
346 | 0 | } |
347 | 0 | else if (len > 5 && strncmp(name, "ptxn.", 5) == 0) { |
348 | 0 | name += 5; |
349 | 0 | len -= 5; |
350 | 0 | desc->scope = SCOPE_TXN; |
351 | 0 | desc->flags |= VDF_PARENT_CTX; |
352 | 0 | } |
353 | 0 | else if (len > 5 && strncmp(name, "preq.", 5) == 0) { |
354 | 0 | name += 5; |
355 | 0 | len -= 5; |
356 | 0 | desc->scope = SCOPE_REQ; |
357 | 0 | desc->flags |= VDF_PARENT_CTX; |
358 | 0 | } |
359 | 0 | else if (len > 5 && strncmp(name, "pres.", 5) == 0) { |
360 | 0 | name += 5; |
361 | 0 | len -= 5; |
362 | 0 | desc->scope = SCOPE_RES; |
363 | 0 | desc->flags |= VDF_PARENT_CTX; |
364 | 0 | } else |
365 | 0 | goto err; |
366 | | |
367 | | /* Check variable name syntax. */ |
368 | 0 | if (!var_name_is_valid(name, len, &off)) { |
369 | 0 | if (off < (size_t)len) |
370 | 0 | memprintf(err, "Invalid character '%c' in variable name %.*s", name[off], |
371 | 0 | len, name); |
372 | 0 | goto err; |
373 | 0 | } |
374 | | |
375 | 0 | desc->name_hash = XXH3(name, len, var_name_hash_seed); |
376 | 0 | desc->name = name; |
377 | 0 | desc->name_len = len; |
378 | 0 | return 1; |
379 | | |
380 | 0 | err: |
381 | 0 | if (!err) |
382 | 0 | memprintf(err, "invalid variable name '%.*s'. A variable name must start with its scope. " |
383 | 0 | "The scope can be 'proc', '(p)sess', '(p)txn', '(p)req', '(p)res' or 'check'", len, name); |
384 | 0 | return 0; |
385 | 0 | } |
386 | | |
387 | | /* This function returns the variable from the given list that matches |
388 | | * <name_hash> or returns NULL if not found. It's only a linked list since it |
389 | | * is not expected to have many variables per scope (a few tens at best). |
390 | | * The caller is responsible for ensuring that <vars> is properly locked. |
391 | | */ |
392 | | static struct var *var_get(struct vars *vars, uint64_t name_hash) |
393 | 0 | { |
394 | 0 | return cebu64_item_lookup(&vars->name_root[name_hash % VAR_NAME_ROOTS], |
395 | 0 | name_node, name_hash, name_hash, struct var); |
396 | 0 | } |
397 | | |
398 | | /* Returns 0 if fails, else returns 1. */ |
399 | | static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private) |
400 | 0 | { |
401 | 0 | const struct var_desc *var_desc = &args[0].data.var; |
402 | 0 | const struct buffer *def = NULL; |
403 | |
|
404 | 0 | if (args[1].type == ARGT_STR) |
405 | 0 | def = &args[1].data.str; |
406 | |
|
407 | 0 | return vars_get_by_desc(var_desc, smp, def); |
408 | 0 | } |
409 | | |
410 | | /* Dumps all variables in the specified scope, optionally filtered by prefix. |
411 | | * Output format: var1=value1, var2=value2, ... |
412 | | * String values are quoted and escaped, binary values are hex-encoded (x...). |
413 | | * Returns 1 on success, 0 on failure (buffer too small). |
414 | | * Note: When using prefix filtering, all variables are still visited, so this |
415 | | * should not be used with configs involving thousands of variables. |
416 | | */ |
417 | | static int smp_fetch_dump_all_vars(const struct arg *args, struct sample *smp, const char *kw, void *private) |
418 | 0 | { |
419 | 0 | struct buffer *output; |
420 | 0 | struct vars *vars; |
421 | 0 | struct var *var; |
422 | 0 | struct var_desc desc; |
423 | 0 | const char *prefix = NULL; |
424 | 0 | size_t prefix_len = 0; |
425 | 0 | const char *delim = ", "; |
426 | 0 | size_t delim_len = 2; |
427 | 0 | int first = 1; |
428 | 0 | int i; |
429 | 0 | int start_scope, end_scope; |
430 | 0 | int cur_scope; |
431 | | |
432 | | /* Get output buffer */ |
433 | 0 | output = get_trash_chunk(); |
434 | 0 | chunk_reset(output); |
435 | | |
436 | | /* Parse arguments */ |
437 | 0 | if (args[0].type == ARGT_SINT) { |
438 | 0 | if (args[0].data.sint == -1) { |
439 | 0 | start_scope = SCOPE_SESS; |
440 | 0 | end_scope = SCOPE_PROC; |
441 | 0 | } else { |
442 | 0 | start_scope = end_scope = args[0].data.sint; |
443 | 0 | } |
444 | 0 | } else { |
445 | | /* Auto-detect scope from context */ |
446 | 0 | if (smp->strm) |
447 | 0 | start_scope = end_scope = SCOPE_TXN; |
448 | 0 | else if (smp->sess) |
449 | 0 | start_scope = end_scope = SCOPE_SESS; |
450 | 0 | else |
451 | 0 | start_scope = end_scope = SCOPE_PROC; |
452 | 0 | } |
453 | | |
454 | | /* Optional prefix filter */ |
455 | 0 | if (args[1].type == ARGT_STR) { |
456 | 0 | prefix = args[1].data.str.area; |
457 | 0 | prefix_len = args[1].data.str.data; |
458 | 0 | } |
459 | | |
460 | | /* Optional delimiter */ |
461 | 0 | if (args[2].type == ARGT_STR) { |
462 | 0 | delim = args[2].data.str.area; |
463 | 0 | delim_len = args[2].data.str.data; |
464 | 0 | } |
465 | |
|
466 | 0 | desc.flags = 0; |
467 | 0 | desc.name_hash = 0; |
468 | |
|
469 | 0 | for (cur_scope = start_scope; cur_scope <= end_scope; cur_scope++) { |
470 | 0 | desc.scope = cur_scope; |
471 | |
|
472 | 0 | vars = get_vars(smp->sess, smp->strm, &desc); |
473 | 0 | if (!vars || vars->scope != desc.scope) |
474 | 0 | continue; |
475 | | |
476 | 0 | vars_rdlock(vars); |
477 | | |
478 | | /* Iterate through all variable roots */ |
479 | 0 | for (i = 0; i < VAR_NAME_ROOTS; i++) { |
480 | 0 | var = cebu64_item_first(&vars->name_root[i], name_node, name_hash, struct var); |
481 | |
|
482 | 0 | while (var) { |
483 | 0 | const char *scope_prefix; |
484 | | |
485 | | /* Check prefix filter */ |
486 | 0 | if (prefix) { |
487 | 0 | if (!var->name || strncmp(var->name, prefix, prefix_len) != 0) { |
488 | 0 | var = cebu64_item_next(&vars->name_root[i], name_node, name_hash, var); |
489 | 0 | continue; |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | /* Add delimiter */ |
494 | 0 | if (!first) { |
495 | 0 | if (output->data + delim_len >= output->size) |
496 | 0 | goto fail_unlock; |
497 | 0 | chunk_memcat(output, delim, delim_len); |
498 | 0 | } |
499 | 0 | first = 0; |
500 | | |
501 | | /* Add variable name with scope prefix */ |
502 | 0 | scope_prefix = var_scope_names[desc.scope]; |
503 | 0 | if (var->name) { |
504 | 0 | if (chunk_appendf(output, "%s%s=", scope_prefix, var->name) < 0) |
505 | 0 | goto fail_unlock; |
506 | 0 | } else { |
507 | 0 | if (chunk_appendf(output, "var_%016llx=", (unsigned long long)var->name_hash) < 0) |
508 | 0 | goto fail_unlock; |
509 | 0 | } |
510 | | |
511 | | /* Convert value based on type */ |
512 | 0 | if (var->data.type == SMP_T_STR) { |
513 | | /* String: quote and escape */ |
514 | 0 | if (chunk_escape_string(output, var->data.u.str.area, var->data.u.str.data) < 0) |
515 | 0 | goto fail_unlock; |
516 | |
|
517 | 0 | } else if (var->data.type == SMP_T_BIN) { |
518 | | /* Binary: hex encode */ |
519 | 0 | if (dump_binary(output, var->data.u.str.area, var->data.u.str.data) != var->data.u.str.data) |
520 | 0 | goto fail_unlock; |
521 | 0 | } else if (var->data.type == SMP_T_SINT) { |
522 | | /* Integer */ |
523 | 0 | if (chunk_appendf(output, "%lld", (long long)var->data.u.sint) < 0) |
524 | 0 | goto fail_unlock; |
525 | |
|
526 | 0 | } else if (var->data.type == SMP_T_BOOL) { |
527 | | /* Boolean */ |
528 | 0 | const char *bool_str = var->data.u.sint ? "true" : "false"; |
529 | 0 | if (chunk_appendf(output, "%s", bool_str) < 0) |
530 | 0 | goto fail_unlock; |
531 | |
|
532 | 0 | } else if (var->data.type == SMP_T_IPV4 || var->data.type == SMP_T_IPV6) { |
533 | | /* Address */ |
534 | 0 | char addr_str[INET6_ADDRSTRLEN]; |
535 | 0 | const char *res; |
536 | |
|
537 | 0 | if (var->data.type == SMP_T_IPV4) |
538 | 0 | res = inet_ntop(AF_INET, &var->data.u.ipv4, addr_str, sizeof(addr_str)); |
539 | 0 | else |
540 | 0 | res = inet_ntop(AF_INET6, &var->data.u.ipv6, addr_str, sizeof(addr_str)); |
541 | |
|
542 | 0 | if (!res) { |
543 | 0 | if (chunk_appendf(output, "(addr)") < 0) |
544 | 0 | goto fail_unlock; |
545 | 0 | } else { |
546 | 0 | if (chunk_appendf(output, "%s", addr_str) < 0) |
547 | 0 | goto fail_unlock; |
548 | 0 | } |
549 | 0 | } else if (var->data.type == SMP_T_METH) { |
550 | | /* HTTP Method */ |
551 | 0 | if (var->data.u.meth.meth == HTTP_METH_OTHER) { |
552 | 0 | if (chunk_escape_string(output, var->data.u.meth.str.area, var->data.u.meth.str.data) < 0) |
553 | 0 | goto fail_unlock; |
554 | 0 | } else { |
555 | 0 | const char *method_str = http_known_methods[var->data.u.meth.meth].ptr; |
556 | 0 | if (chunk_appendf(output, "\"%s\"", method_str) < 0) |
557 | 0 | goto fail_unlock; |
558 | 0 | } |
559 | 0 | } else { |
560 | | /* Other types: show type number */ |
561 | 0 | if (chunk_appendf(output, "(type:%d)", var->data.type) < 0) |
562 | 0 | goto fail_unlock; |
563 | 0 | } |
564 | | |
565 | 0 | var = cebu64_item_next(&vars->name_root[i], name_node, name_hash, var); |
566 | 0 | } |
567 | 0 | } |
568 | | |
569 | 0 | vars_rdunlock(vars); |
570 | 0 | } |
571 | | |
572 | | /* Set output sample */ |
573 | 0 | smp->data.type = SMP_T_STR; |
574 | 0 | smp->data.u.str.area = output->area; |
575 | 0 | smp->data.u.str.data = output->data; |
576 | 0 | smp->flags &= ~SMP_F_CONST; |
577 | |
|
578 | 0 | return 1; |
579 | | |
580 | 0 | fail_unlock: |
581 | 0 | vars_rdunlock(vars); |
582 | 0 | output->data = 0; |
583 | 0 | return 0; |
584 | 0 | } |
585 | | |
586 | | /* |
587 | | * Clear the contents of a variable so that it can be reset directly. |
588 | | * This function is used just before a variable is filled out of a sample's |
589 | | * content. |
590 | | */ |
591 | | static inline void var_clear_buffer(struct sample *smp, struct vars *vars, struct var *var, int var_type) |
592 | 0 | { |
593 | 0 | if (var_type == SMP_T_STR || var_type == SMP_T_BIN) { |
594 | 0 | ha_free(&var->data.u.str.area); |
595 | 0 | var_accounting_diff(vars, smp->sess, smp->strm, |
596 | 0 | -var->data.u.str.data); |
597 | 0 | } |
598 | 0 | else if (var_type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) { |
599 | 0 | ha_free(&var->data.u.meth.str.area); |
600 | 0 | var_accounting_diff(vars, smp->sess, smp->strm, |
601 | 0 | -var->data.u.meth.str.data); |
602 | 0 | } |
603 | 0 | } |
604 | | |
605 | | /* This function tries to create a variable whose name hash is <name_hash> in |
606 | | * scope <scope> and store sample <smp> as its value. |
607 | | * |
608 | | * The stream and session are extracted from <smp>, whose stream may be NULL |
609 | | * when scope is SCOPE_SESS. In case there wouldn't be enough memory to store |
610 | | * the sample while the variable was already created, it would be changed to |
611 | | * a bool (which is memory-less). |
612 | | * |
613 | | * Flags is a bitfield that may contain one of the following flags: |
614 | | * - VF_CREATEONLY: do nothing if the variable already exists (success). |
615 | | * - VF_PERMANENT: this flag will be passed to the variable upon creation |
616 | | * |
617 | | * - VF_COND_IFEXISTS: only set variable if it already exists |
618 | | * - VF_COND_IFNOTEXISTS: only set variable if it did not exist yet |
619 | | * - VF_COND_IFEMPTY: only set variable if sample is empty |
620 | | * - VF_COND_IFNOTEMPTY: only set variable if sample is not empty |
621 | | * - VF_COND_IFSET: only set variable if its type is not SMP_TYPE_ANY |
622 | | * - VF_COND_IFNOTSET: only set variable if its type is ANY |
623 | | * - VF_COND_IFGT: only set variable if its value is greater than the sample's |
624 | | * - VF_COND_IFLT: only set variable if its value is less than the sample's |
625 | | * |
626 | | * It returns 0 on failure, non-zero on success. |
627 | | */ |
628 | | int var_set(const struct var_desc *desc, struct sample *smp, uint flags) |
629 | 0 | { |
630 | 0 | struct vars *vars; |
631 | 0 | struct var *var; |
632 | 0 | int ret = 0; |
633 | 0 | int previous_type = SMP_T_ANY; |
634 | |
|
635 | 0 | if (!desc || (desc->flags & VDF_PARENT_CTX)) |
636 | 0 | return 0; |
637 | | |
638 | 0 | vars = get_vars(smp->sess, smp->strm, desc); |
639 | 0 | if (!vars || vars->scope != desc->scope) |
640 | 0 | return 0; |
641 | | |
642 | 0 | vars_wrlock(vars); |
643 | | |
644 | | /* Look for existing variable name. */ |
645 | 0 | var = var_get(vars, desc->name_hash); |
646 | |
|
647 | 0 | if (var) { |
648 | 0 | if (flags & VF_CREATEONLY) { |
649 | 0 | ret = 1; |
650 | 0 | goto unlock; |
651 | 0 | } |
652 | | |
653 | 0 | if (flags & VF_COND_IFNOTEXISTS) |
654 | 0 | goto unlock; |
655 | 0 | } else { |
656 | 0 | if (flags & VF_COND_IFEXISTS) |
657 | 0 | goto unlock; |
658 | | |
659 | | /* Check memory available. */ |
660 | 0 | if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var))) |
661 | 0 | goto unlock; |
662 | | |
663 | | /* Create new entry. */ |
664 | 0 | var = pool_alloc(var_pool); |
665 | 0 | if (!var) |
666 | 0 | goto unlock; |
667 | 0 | var->name_hash = desc->name_hash; |
668 | 0 | var->flags = flags & VF_PERMANENT; |
669 | | |
670 | | /* Save variable name */ |
671 | 0 | var->name = NULL; |
672 | 0 | if (desc->name && desc->name_len > 0) { |
673 | 0 | var->name = my_strndup(desc->name, desc->name_len); |
674 | 0 | if (!var->name) { |
675 | 0 | pool_free(var_pool, var); |
676 | 0 | goto unlock; |
677 | 0 | } |
678 | 0 | } |
679 | 0 | var->data.type = SMP_T_ANY; |
680 | 0 | cebu64_item_insert(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], name_node, name_hash, var); |
681 | 0 | } |
682 | | |
683 | | /* A variable of type SMP_T_ANY is considered as unset (either created |
684 | | * and never set or unset-var was called on it). |
685 | | */ |
686 | 0 | if ((flags & VF_COND_IFSET && var->data.type == SMP_T_ANY) || |
687 | 0 | (flags & VF_COND_IFNOTSET && var->data.type != SMP_T_ANY)) |
688 | 0 | goto unlock; |
689 | | |
690 | | /* Set type. */ |
691 | 0 | previous_type = var->data.type; |
692 | 0 | var->data.type = smp->data.type; |
693 | |
|
694 | 0 | if (flags & VF_COND_IFEMPTY) { |
695 | 0 | switch(smp->data.type) { |
696 | 0 | case SMP_T_ANY: |
697 | 0 | case SMP_T_STR: |
698 | 0 | case SMP_T_BIN: |
699 | | /* The actual test on the contents of the sample will be |
700 | | * performed later. |
701 | | */ |
702 | 0 | break; |
703 | 0 | default: |
704 | | /* The sample cannot be empty since it has a scalar type. */ |
705 | 0 | var->data.type = previous_type; |
706 | 0 | goto unlock; |
707 | 0 | } |
708 | 0 | } |
709 | | |
710 | | /* Copy data. If the data needs memory, the function can fail. */ |
711 | 0 | switch (var->data.type) { |
712 | 0 | case SMP_T_BOOL: |
713 | 0 | var_clear_buffer(smp, vars, var, previous_type); |
714 | 0 | var->data.u.sint = smp->data.u.sint; |
715 | 0 | break; |
716 | 0 | case SMP_T_SINT: |
717 | 0 | if (previous_type == var->data.type) { |
718 | 0 | if (((flags & VF_COND_IFGT) && !(var->data.u.sint > smp->data.u.sint)) || |
719 | 0 | ((flags & VF_COND_IFLT) && !(var->data.u.sint < smp->data.u.sint))) |
720 | 0 | goto unlock; |
721 | 0 | } |
722 | 0 | var_clear_buffer(smp, vars, var, previous_type); |
723 | 0 | var->data.u.sint = smp->data.u.sint; |
724 | 0 | break; |
725 | 0 | case SMP_T_IPV4: |
726 | 0 | var_clear_buffer(smp, vars, var, previous_type); |
727 | 0 | var->data.u.ipv4 = smp->data.u.ipv4; |
728 | 0 | break; |
729 | 0 | case SMP_T_IPV6: |
730 | 0 | var_clear_buffer(smp, vars, var, previous_type); |
731 | 0 | var->data.u.ipv6 = smp->data.u.ipv6; |
732 | 0 | break; |
733 | 0 | case SMP_T_STR: |
734 | 0 | case SMP_T_BIN: |
735 | 0 | if ((flags & VF_COND_IFNOTEMPTY && !smp->data.u.str.data) || |
736 | 0 | (flags & VF_COND_IFEMPTY && smp->data.u.str.data)) { |
737 | 0 | var->data.type = previous_type; |
738 | 0 | goto unlock; |
739 | 0 | } |
740 | 0 | var_clear_buffer(smp, vars, var, previous_type); |
741 | 0 | if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) { |
742 | 0 | var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */ |
743 | 0 | goto unlock; |
744 | 0 | } |
745 | | |
746 | 0 | var->data.u.str.area = malloc(smp->data.u.str.data); |
747 | 0 | if (!var->data.u.str.area) { |
748 | 0 | var_accounting_diff(vars, smp->sess, smp->strm, |
749 | 0 | -smp->data.u.str.data); |
750 | 0 | var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */ |
751 | 0 | goto unlock; |
752 | 0 | } |
753 | 0 | var->data.u.str.data = smp->data.u.str.data; |
754 | 0 | memcpy(var->data.u.str.area, smp->data.u.str.area, |
755 | 0 | var->data.u.str.data); |
756 | 0 | break; |
757 | 0 | case SMP_T_METH: |
758 | 0 | var_clear_buffer(smp, vars, var, previous_type); |
759 | 0 | var->data.u.meth.meth = smp->data.u.meth.meth; |
760 | 0 | if (smp->data.u.meth.meth != HTTP_METH_OTHER) |
761 | 0 | break; |
762 | | |
763 | 0 | if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) { |
764 | 0 | var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */ |
765 | 0 | goto unlock; |
766 | 0 | } |
767 | | |
768 | 0 | var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data); |
769 | 0 | if (!var->data.u.meth.str.area) { |
770 | 0 | var_accounting_diff(vars, smp->sess, smp->strm, |
771 | 0 | -smp->data.u.meth.str.data); |
772 | 0 | var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */ |
773 | 0 | goto unlock; |
774 | 0 | } |
775 | 0 | var->data.u.meth.str.data = smp->data.u.meth.str.data; |
776 | 0 | var->data.u.meth.str.size = smp->data.u.meth.str.data; |
777 | 0 | memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area, |
778 | 0 | var->data.u.meth.str.data); |
779 | 0 | break; |
780 | 0 | } |
781 | | |
782 | | /* OK, now done */ |
783 | 0 | ret = 1; |
784 | 0 | unlock: |
785 | 0 | vars_wrunlock(vars); |
786 | 0 | return ret; |
787 | 0 | } |
788 | | |
789 | | /* Deletes a variable matching name hash <name_hash> and scope <scope> for the |
790 | | * session and stream found in <smp>. Note that stream may be null for |
791 | | * SCOPE_SESS. Returns 0 if the scope was not found otherwise 1. |
792 | | */ |
793 | | int var_unset(const struct var_desc *desc, struct sample *smp) |
794 | 0 | { |
795 | 0 | struct vars *vars; |
796 | 0 | struct var *var; |
797 | 0 | unsigned int size = 0; |
798 | |
|
799 | 0 | if (!desc || (desc->flags & VDF_PARENT_CTX)) |
800 | 0 | return 0; |
801 | | |
802 | 0 | vars = get_vars(smp->sess, smp->strm, desc); |
803 | 0 | if (!vars || vars->scope != desc->scope) |
804 | 0 | return 0; |
805 | | |
806 | | /* Look for existing variable name. */ |
807 | 0 | vars_wrlock(vars); |
808 | 0 | var = var_get(vars, desc->name_hash); |
809 | 0 | if (var) { |
810 | 0 | size = var_clear(vars, var, 0); |
811 | 0 | var_accounting_diff(vars, smp->sess, smp->strm, -size); |
812 | 0 | } |
813 | 0 | vars_wrunlock(vars); |
814 | 0 | return 1; |
815 | 0 | } |
816 | | |
817 | | |
818 | | /* |
819 | | * Convert a string set-var condition into its numerical value. |
820 | | * The corresponding bit is set in the <cond_bitmap> parameter if the |
821 | | * <cond> is known. |
822 | | * Returns 1 in case of success. |
823 | | */ |
824 | | static int vars_parse_cond_param(const struct buffer *cond, uint *cond_bitmap, char **err) |
825 | 0 | { |
826 | 0 | struct var_set_condition *cond_elt = &conditions_array[0]; |
827 | | |
828 | | /* The conditions array is NULL terminated. */ |
829 | 0 | while (cond_elt->cond_str) { |
830 | 0 | if (chunk_strcmp(cond, cond_elt->cond_str) == 0) { |
831 | 0 | *cond_bitmap |= cond_elt->flag; |
832 | 0 | break; |
833 | 0 | } |
834 | 0 | ++cond_elt; |
835 | 0 | } |
836 | |
|
837 | 0 | if (cond_elt->cond_str == NULL && err) |
838 | 0 | memprintf(err, "unknown condition \"%.*s\"", (int)cond->data, cond->area); |
839 | |
|
840 | 0 | return cond_elt->cond_str != NULL; |
841 | 0 | } |
842 | | |
843 | | /* Returns 0 if fails, else returns 1. */ |
844 | | static int smp_conv_store(const struct arg *args, struct sample *smp, void *private) |
845 | 0 | { |
846 | 0 | uint conditions = 0; |
847 | 0 | int cond_idx = 1; |
848 | |
|
849 | 0 | while (args[cond_idx].type == ARGT_STR) { |
850 | 0 | if (vars_parse_cond_param(&args[cond_idx++].data.str, &conditions, NULL) == 0) |
851 | 0 | break; |
852 | 0 | } |
853 | |
|
854 | 0 | return var_set(&args[0].data.var, smp, conditions); |
855 | 0 | } |
856 | | |
857 | | /* Returns 0 if fails, else returns 1. */ |
858 | | static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private) |
859 | 0 | { |
860 | 0 | return var_unset(&args[0].data.var, smp); |
861 | 0 | } |
862 | | |
863 | | /* This functions check an argument entry and fill it with a variable |
864 | | * type. The argument must be a string. If the variable lookup fails, |
865 | | * the function returns 0 and fill <err>, otherwise it returns 1. |
866 | | */ |
867 | | int vars_check_arg(struct arg *arg, char **err) |
868 | 0 | { |
869 | 0 | struct sample empty_smp = { }; |
870 | 0 | struct var_desc desc; |
871 | 0 | char *saved_name = NULL; |
872 | | |
873 | | /* Check arg type. */ |
874 | 0 | if (arg->type != ARGT_STR) { |
875 | 0 | memprintf(err, "unexpected argument type"); |
876 | 0 | return 0; |
877 | 0 | } |
878 | | |
879 | | /* Register new variable name. */ |
880 | 0 | if (!vars_fill_desc(arg->data.str.area, arg->data.str.data, &desc, err)) |
881 | 0 | return 0; |
882 | | |
883 | | /* Save variable name before destroying the chunk */ |
884 | 0 | if (desc.name && desc.name_len > 0) { |
885 | 0 | saved_name = my_strndup(desc.name, desc.name_len); |
886 | 0 | if (!saved_name) { |
887 | 0 | memprintf(err, "out of memory"); |
888 | 0 | return 0; |
889 | 0 | } |
890 | | |
891 | 0 | desc.name = saved_name; |
892 | 0 | desc.flags |= VDF_NAME_ALLOCATED; |
893 | 0 | } |
894 | | |
895 | 0 | if (desc.scope == SCOPE_PROC && !var_set(&desc, &empty_smp, VF_CREATEONLY|VF_PERMANENT)) { |
896 | 0 | if (desc.flags & VDF_NAME_ALLOCATED) |
897 | 0 | ha_free(&saved_name); |
898 | 0 | return 0; |
899 | 0 | } |
900 | | |
901 | | /* properly destroy the chunk */ |
902 | 0 | chunk_destroy(&arg->data.str); |
903 | | |
904 | | /* Use the global variable name pointer. */ |
905 | 0 | arg->type = ARGT_VAR; |
906 | 0 | arg->data.var = desc; /* desc.name already points to saved_name */ |
907 | 0 | return 1; |
908 | 0 | } |
909 | | |
910 | | /* This function stores a sample in a variable unless it is of type "proc" and |
911 | | * not defined yet. |
912 | | * Returns zero on failure and non-zero otherwise. The variable not being |
913 | | * defined is treated as a failure. |
914 | | */ |
915 | | int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp) |
916 | 0 | { |
917 | 0 | struct var_desc desc; |
918 | | |
919 | | /* Resolve name and scope. */ |
920 | 0 | if (!vars_fill_desc(name, len, &desc, NULL)) |
921 | 0 | return 0; |
922 | | |
923 | | /* Variable creation is allowed for all scopes apart from the PROC one. */ |
924 | 0 | return var_set(&desc, smp, (desc.scope == SCOPE_PROC) ? VF_COND_IFEXISTS : 0); |
925 | 0 | } |
926 | | |
927 | | |
928 | | /* This function stores a sample in a variable. |
929 | | * Returns zero on failure and non-zero otherwise. |
930 | | */ |
931 | | int vars_set_by_name(const char *name, size_t len, struct sample *smp) |
932 | 0 | { |
933 | 0 | struct var_desc desc; |
934 | | |
935 | | /* Resolve name and scope. */ |
936 | 0 | if (!vars_fill_desc(name, len, &desc, NULL)) |
937 | 0 | return 0; |
938 | | |
939 | 0 | return var_set(&desc, smp, 0); |
940 | 0 | } |
941 | | |
942 | | /* This function unsets a variable if it was already defined. |
943 | | * Returns zero on failure and non-zero otherwise. |
944 | | */ |
945 | | int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp) |
946 | 0 | { |
947 | 0 | struct var_desc desc; |
948 | | |
949 | | /* Resolve name and scope. */ |
950 | 0 | if (!vars_fill_desc(name, len, &desc, NULL)) |
951 | 0 | return 0; |
952 | | |
953 | 0 | return var_unset(&desc, smp); |
954 | 0 | } |
955 | | |
956 | | |
957 | | /* This retrieves variable whose hash matches <name_hash> from variables <vars>, |
958 | | * and if found and not empty, duplicates the result into sample <smp>. |
959 | | * smp_dup() is used in order to release the variables lock ASAP (so a pre- |
960 | | * allocated chunk is obtained via get_trash_shunk()). The variables' lock is |
961 | | * used for reads. |
962 | | * |
963 | | * The function returns 0 if the variable was not found and no default |
964 | | * value was provided in <def>, otherwise 1 with the sample filled. |
965 | | * Default values are always returned as strings. |
966 | | */ |
967 | | static int var_to_smp(struct vars *vars, uint64_t name_hash, struct sample *smp, const struct buffer *def) |
968 | 0 | { |
969 | 0 | struct var *var; |
970 | | |
971 | | /* Get the variable entry. */ |
972 | 0 | vars_rdlock(vars); |
973 | 0 | var = var_get(vars, name_hash); |
974 | 0 | if (!var || !var->data.type) { |
975 | 0 | if (!def) { |
976 | 0 | vars_rdunlock(vars); |
977 | 0 | return 0; |
978 | 0 | } |
979 | | |
980 | | /* not found but we have a default value */ |
981 | 0 | smp->data.type = SMP_T_STR; |
982 | 0 | smp->data.u.str = *def; |
983 | 0 | } |
984 | 0 | else |
985 | 0 | smp->data = var->data; |
986 | | |
987 | | /* Copy sample. */ |
988 | 0 | smp_dup(smp); |
989 | |
|
990 | 0 | vars_rdunlock(vars); |
991 | 0 | return 1; |
992 | 0 | } |
993 | | |
994 | | /* This function fills a sample with the variable content. |
995 | | * |
996 | | * Keep in mind that a sample content is duplicated by using smp_dup() |
997 | | * and it therefore uses a pre-allocated trash chunk as returned by |
998 | | * get_trash_chunk(). |
999 | | * |
1000 | | * If the variable is not valid in this scope, 0 is always returned. |
1001 | | * If the variable is valid but not found, either the default value |
1002 | | * <def> is returned if not NULL, or zero is returned. |
1003 | | * |
1004 | | * Returns 1 if the sample is filled, otherwise it returns 0. |
1005 | | */ |
1006 | | int vars_get_by_name(const char *name, size_t len, struct sample *smp, const struct buffer *def) |
1007 | 0 | { |
1008 | 0 | struct vars *vars; |
1009 | 0 | struct var_desc desc; |
1010 | | |
1011 | | /* Resolve name and scope. */ |
1012 | 0 | if (!vars_fill_desc(name, len, &desc, NULL)) |
1013 | 0 | return 0; |
1014 | | |
1015 | | /* Select "vars" pool according with the scope. */ |
1016 | 0 | vars = get_vars(smp->sess, smp->strm, &desc); |
1017 | 0 | if (!vars || vars->scope != desc.scope) |
1018 | 0 | return 0; |
1019 | | |
1020 | 0 | return var_to_smp(vars, desc.name_hash, smp, def); |
1021 | 0 | } |
1022 | | |
1023 | | /* This function fills a sample with the content of the variable described |
1024 | | * by <var_desc>. |
1025 | | * |
1026 | | * Keep in mind that a sample content is duplicated by using smp_dup() |
1027 | | * and it therefore uses a pre-allocated trash chunk as returned by |
1028 | | * get_trash_chunk(). |
1029 | | * |
1030 | | * If the variable is not valid in this scope, 0 is always returned. |
1031 | | * If the variable is valid but not found, either the default value |
1032 | | * <def> is returned if not NULL, or zero is returned. |
1033 | | * |
1034 | | * Returns 1 if the sample is filled, otherwise it returns 0. |
1035 | | */ |
1036 | | int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp, const struct buffer *def) |
1037 | 0 | { |
1038 | 0 | struct vars *vars; |
1039 | | |
1040 | | /* Select "vars" pool according with the scope. */ |
1041 | 0 | vars = get_vars(smp->sess, smp->strm, var_desc); |
1042 | | |
1043 | | /* Check if the scope is available a this point of processing. */ |
1044 | 0 | if (!vars || vars->scope != var_desc->scope) |
1045 | 0 | return 0; |
1046 | | |
1047 | 0 | return var_to_smp(vars, var_desc->name_hash, smp, def); |
1048 | 0 | } |
1049 | | |
1050 | | /* Always returns ACT_RET_CONT even if an error occurs. */ |
1051 | | static enum act_return action_store(struct act_rule *rule, struct proxy *px, |
1052 | | struct session *sess, struct stream *s, int flags) |
1053 | 0 | { |
1054 | 0 | struct buffer *fmtstr = NULL; |
1055 | 0 | struct sample smp; |
1056 | 0 | int dir; |
1057 | |
|
1058 | 0 | switch (rule->from) { |
1059 | 0 | case ACT_F_TCP_REQ_CON: dir = SMP_OPT_DIR_REQ; break; |
1060 | 0 | case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break; |
1061 | 0 | case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break; |
1062 | 0 | case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break; |
1063 | 0 | case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break; |
1064 | 0 | case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break; |
1065 | 0 | case ACT_F_TCP_CHK: dir = SMP_OPT_DIR_REQ; break; |
1066 | 0 | case ACT_F_CFG_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */ |
1067 | 0 | case ACT_F_CLI_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */ |
1068 | 0 | default: |
1069 | 0 | send_log(px, LOG_ERR, "Vars: internal error while execute action store."); |
1070 | 0 | if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) |
1071 | 0 | ha_alert("Vars: internal error while execute action store.\n"); |
1072 | 0 | return ACT_RET_CONT; |
1073 | 0 | } |
1074 | | |
1075 | | /* Process the expression. */ |
1076 | 0 | memset(&smp, 0, sizeof(smp)); |
1077 | |
|
1078 | 0 | if (rule->action == 2) { /* set-var-fmt */ |
1079 | | /* a format-string is used */ |
1080 | |
|
1081 | 0 | fmtstr = alloc_trash_chunk(); |
1082 | 0 | if (!fmtstr) { |
1083 | 0 | send_log(px, LOG_ERR, "Vars: memory allocation failure while processing store rule."); |
1084 | 0 | if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) |
1085 | 0 | ha_alert("Vars: memory allocation failure while processing store rule.\n"); |
1086 | 0 | return ACT_RET_CONT; |
1087 | 0 | } |
1088 | | |
1089 | | /* execute the log-format expression */ |
1090 | 0 | fmtstr->data = sess_build_logline(sess, s, fmtstr->area, fmtstr->size, &rule->arg.vars.fmt); |
1091 | | |
1092 | | /* convert it to a sample of type string as it's what the vars |
1093 | | * API consumes, and store it. |
1094 | | */ |
1095 | 0 | smp_set_owner(&smp, px, sess, s, 0); |
1096 | 0 | smp.data.type = SMP_T_STR; |
1097 | 0 | smp.data.u.str = *fmtstr; |
1098 | 0 | } |
1099 | 0 | else { /* set-var */ |
1100 | | /* an expression is used */ |
1101 | 0 | if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL, |
1102 | 0 | rule->arg.vars.expr, &smp)) |
1103 | 0 | return ACT_RET_CONT; |
1104 | 0 | } |
1105 | | |
1106 | | /* Store the sample, and ignore errors. */ |
1107 | 0 | var_set(&rule->arg.vars.desc, &smp, rule->arg.vars.conditions); |
1108 | 0 | free_trash_chunk(fmtstr); |
1109 | 0 | return ACT_RET_CONT; |
1110 | 0 | } |
1111 | | |
1112 | | /* Always returns ACT_RET_CONT even if an error occurs. */ |
1113 | | static enum act_return action_clear(struct act_rule *rule, struct proxy *px, |
1114 | | struct session *sess, struct stream *s, int flags) |
1115 | 0 | { |
1116 | 0 | struct sample smp; |
1117 | |
|
1118 | 0 | memset(&smp, 0, sizeof(smp)); |
1119 | 0 | smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL); |
1120 | | |
1121 | | /* Clear the variable using the sample context, and ignore errors. */ |
1122 | 0 | var_unset(&rule->arg.vars.desc, &smp); |
1123 | 0 | return ACT_RET_CONT; |
1124 | 0 | } |
1125 | | |
1126 | | static void release_store_rule(struct act_rule *rule) |
1127 | 0 | { |
1128 | 0 | lf_expr_deinit(&rule->arg.vars.fmt); |
1129 | |
|
1130 | 0 | release_sample_expr(rule->arg.vars.expr); |
1131 | | |
1132 | | /* Free variable name if allocated */ |
1133 | 0 | if (rule->arg.vars.desc.flags & VDF_NAME_ALLOCATED) |
1134 | 0 | ha_free((char **)&rule->arg.vars.desc.name); |
1135 | 0 | } |
1136 | | |
1137 | | /* |
1138 | | * Cast a string sample to an integer, but unlike c_str2int() reject any |
1139 | | * trailing garbage after the number. |
1140 | | */ |
1141 | | static int c_str2int_strict(struct sample *smp) |
1142 | 0 | { |
1143 | 0 | const char *str; |
1144 | 0 | const char *end; |
1145 | |
|
1146 | 0 | if (smp->data.u.str.data == 0) |
1147 | 0 | return 0; |
1148 | | |
1149 | 0 | str = smp->data.u.str.area; |
1150 | 0 | end = smp->data.u.str.area + smp->data.u.str.data; |
1151 | |
|
1152 | 0 | smp->data.u.sint = read_int64(&str, end); |
1153 | 0 | if (str != end) |
1154 | 0 | return 0; |
1155 | 0 | smp->data.type = SMP_T_SINT; |
1156 | 0 | smp->flags &= ~SMP_F_CONST; |
1157 | 0 | return 1; |
1158 | 0 | } |
1159 | | |
1160 | | /* |
1161 | | * Cast a string sample to a boolean. The textual "true"/"false" spellings |
1162 | | * are accepted in any case, as are the strict integers 0 and 1. Anything else |
1163 | | * is rejected. |
1164 | | */ |
1165 | | static int c_str2bool(struct sample *smp) |
1166 | 0 | { |
1167 | 0 | if (strcasecmp(smp->data.u.str.area, "true") == 0) { |
1168 | 0 | smp->data.type = SMP_T_BOOL; |
1169 | 0 | smp->data.u.sint = 1; |
1170 | 0 | smp->flags &= ~SMP_F_CONST; |
1171 | 0 | return 1; |
1172 | 0 | } |
1173 | 0 | if (strcasecmp(smp->data.u.str.area, "false") == 0) { |
1174 | 0 | smp->data.type = SMP_T_BOOL; |
1175 | 0 | smp->data.u.sint = 0; |
1176 | 0 | smp->flags &= ~SMP_F_CONST; |
1177 | 0 | return 1; |
1178 | 0 | } |
1179 | | |
1180 | 0 | if (!c_str2int_strict(smp)) |
1181 | 0 | return 0; |
1182 | | |
1183 | 0 | if (smp->data.u.sint != 0 && smp->data.u.sint != 1) |
1184 | 0 | return 0; |
1185 | | |
1186 | 0 | smp->data.type = SMP_T_BOOL; |
1187 | 0 | return 1; |
1188 | 0 | } |
1189 | | |
1190 | | /* |
1191 | | * Conversion array used by set-vars-from-map(). Map values are always parsed as |
1192 | | * strings. Unlike the global sample_casts matrix, string-to-number conversion |
1193 | | * uses the strict variants above. |
1194 | | */ |
1195 | | static sample_cast_fct svfm_sample_casts[SMP_TYPES] = { |
1196 | | [SMP_T_STR] = c_none, |
1197 | | [SMP_T_BOOL] = c_str2bool, |
1198 | | [SMP_T_SINT] = c_str2int_strict, |
1199 | | }; |
1200 | | |
1201 | | /* |
1202 | | * Same as sample_convert() but based on the local svfm_sample_casts array |
1203 | | * above. |
1204 | | */ |
1205 | | static int svfm_convert(struct sample *sample, int req_type) |
1206 | 0 | { |
1207 | 0 | if (!svfm_sample_casts[req_type]) |
1208 | 0 | return 0; |
1209 | 0 | if (svfm_sample_casts[req_type] == c_none) |
1210 | 0 | return 1; |
1211 | 0 | return svfm_sample_casts[req_type](sample); |
1212 | 0 | } |
1213 | | |
1214 | | /* |
1215 | | * Parses the map-entry value string <str> into sample <smp> according to the |
1216 | | * requested sample data type <type>. |
1217 | | * Returns 0 on success, 1 on failure. For strings the sample references <str> |
1218 | | * directly (flagged const), so <str> must outlive the sample; here it always |
1219 | | * does since it points into the map entry, which lives as long as the process. |
1220 | | */ |
1221 | | static int svfm_parse_value(const char *str, int type, struct sample *smp) |
1222 | 0 | { |
1223 | 0 | smp->data.type = SMP_T_STR; |
1224 | 0 | smp->flags |= SMP_F_CONST; |
1225 | 0 | smp->data.u.str.area = (char *)str; |
1226 | 0 | smp->data.u.str.data = strlen(str); |
1227 | 0 | smp->data.u.str.size = 0; /* const, not writable */ |
1228 | |
|
1229 | 0 | if (!svfm_convert(smp, type)) |
1230 | 0 | return 1; |
1231 | | |
1232 | 0 | return 0; |
1233 | 0 | } |
1234 | | |
1235 | | /* |
1236 | | * Runtime handler for the "set-vars-from-map" action. It walks the current |
1237 | | * generation of the map entries and, for each one, sets a variable named |
1238 | | * "<scope>.<key>" to the value parsed from the entry according to the |
1239 | | * configured type. It always returns ACT_RET_CONT, ignoring per-entry errors |
1240 | | * so that a single malformed line does not abort the whole batch. |
1241 | | */ |
1242 | | static enum act_return action_set_vars_from_map(struct act_rule *rule, struct proxy *px, |
1243 | | struct session *sess, struct stream *s, int flags) |
1244 | 0 | { |
1245 | 0 | struct pat_ref *ref = rule->arg.act.p[0]; |
1246 | 0 | enum vars_scope scope = (enum vars_scope)(uintptr_t)rule->arg.act.p[1]; |
1247 | 0 | int type = (int)(uintptr_t)rule->arg.act.p[2]; |
1248 | 0 | unsigned int count = 0; |
1249 | |
|
1250 | 0 | struct pat_ref_gen *gen; |
1251 | 0 | struct pat_ref_elt *elt; |
1252 | |
|
1253 | 0 | HA_RWLOCK_RDLOCK(PATREF_LOCK, &ref->lock); |
1254 | |
|
1255 | 0 | gen = pat_ref_gen_get(ref, ref->curr_gen); |
1256 | 0 | if (!gen) |
1257 | 0 | goto done; |
1258 | | |
1259 | 0 | list_for_each_entry(elt, &gen->head, list) { |
1260 | 0 | struct var_desc desc; |
1261 | 0 | struct sample smp; |
1262 | 0 | const char *value = elt->sample ? elt->sample : ""; |
1263 | 0 | size_t name_len = strlen(elt->pattern); |
1264 | | |
1265 | | /* Too many entries processed, stop */ |
1266 | 0 | if (++count > svfm_max) |
1267 | 0 | break; |
1268 | | |
1269 | | /* Silently ignore entries whose name would not be a valid |
1270 | | * variable name, as well as empty names. |
1271 | | */ |
1272 | 0 | if (!var_name_is_valid(elt->pattern, name_len, NULL)) |
1273 | 0 | continue; |
1274 | | |
1275 | | /* The scope was already resolved at parse time, so the |
1276 | | * descriptor can be filled directly from the map key without |
1277 | | * going through vars_fill_desc() again. |
1278 | | */ |
1279 | 0 | desc.scope = scope; |
1280 | 0 | desc.flags = 0; |
1281 | 0 | desc.name = elt->pattern; |
1282 | 0 | desc.name_len = name_len; |
1283 | 0 | desc.name_hash = XXH3(elt->pattern, name_len, var_name_hash_seed); |
1284 | |
|
1285 | 0 | memset(&smp, 0, sizeof(smp)); |
1286 | 0 | smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL); |
1287 | |
|
1288 | 0 | if (svfm_parse_value(value, type, &smp)) |
1289 | | /* Value parsing failed, skipping */ |
1290 | 0 | continue; |
1291 | | |
1292 | 0 | var_set(&desc, &smp, 0); |
1293 | 0 | } |
1294 | |
|
1295 | 0 | done: |
1296 | 0 | HA_RWLOCK_RDUNLOCK(PATREF_LOCK, &ref->lock); |
1297 | 0 | return ACT_RET_CONT; |
1298 | 0 | } |
1299 | | |
1300 | | /* This two function checks the variable name and replace the |
1301 | | * configuration string name by the global string name. its |
1302 | | * the same string, but the global pointer can be easy to |
1303 | | * compare. They return non-zero on success, zero on failure. |
1304 | | * |
1305 | | * The first function checks a sample-fetch and the second |
1306 | | * checks a converter. |
1307 | | */ |
1308 | | static int smp_check_var(struct arg *args, char **err) |
1309 | 0 | { |
1310 | 0 | return vars_check_arg(&args[0], err); |
1311 | 0 | } |
1312 | | |
1313 | | /* This function checks all arguments for dump_all_vars() |
1314 | | * Args: [scope], [prefix], [delimiter] |
1315 | | * Both arguments are optional |
1316 | | */ |
1317 | | static int smp_check_dump_all_vars(struct arg *args, char **err) |
1318 | 0 | { |
1319 | | /* First argument (scope) is optional */ |
1320 | |
|
1321 | 0 | if (args[0].type == ARGT_STR) { |
1322 | 0 | const char *scope = args[0].data.str.area; |
1323 | 0 | int scope_id = -1; |
1324 | 0 | int i; |
1325 | 0 | char buf[16]; |
1326 | |
|
1327 | 0 | if (args[0].data.str.data == 0) { |
1328 | 0 | chunk_destroy(&args[0].data.str); |
1329 | 0 | args[0].type = ARGT_SINT; |
1330 | 0 | args[0].data.sint = scope_id; |
1331 | 0 | return 1; |
1332 | 0 | } |
1333 | | |
1334 | 0 | if (args[0].data.str.data < sizeof(buf) - 1) { |
1335 | 0 | snprintf(buf, sizeof(buf), "%s.", scope); |
1336 | |
|
1337 | 0 | for (i = 0; i <= SCOPE_CHECK; i++) { |
1338 | 0 | if (strcmp(buf, var_scope_names[i]) == 0) { |
1339 | 0 | scope_id = i; |
1340 | 0 | break; |
1341 | 0 | } |
1342 | 0 | } |
1343 | 0 | } |
1344 | |
|
1345 | 0 | if (scope_id == -1) { |
1346 | 0 | memprintf(err, "invalid scope '%s', must be one of: sess, txn, req, res, proc", scope); |
1347 | 0 | return 0; |
1348 | 0 | } |
1349 | | |
1350 | 0 | chunk_destroy(&args[0].data.str); |
1351 | 0 | args[0].type = ARGT_SINT; |
1352 | 0 | args[0].data.sint = scope_id; |
1353 | 0 | } |
1354 | 0 | else if (args[0].type != ARGT_STOP) { |
1355 | 0 | memprintf(err, "first argument must be a string (scope) or omitted"); |
1356 | 0 | return 0; |
1357 | 0 | } else { |
1358 | 0 | args[0].type = ARGT_SINT; |
1359 | 0 | args[0].data.sint = -1; |
1360 | 0 | } |
1361 | | |
1362 | | /* Second argument (prefix) is optional */ |
1363 | 0 | if (args[1].type != ARGT_STR && args[1].type != ARGT_STOP) { |
1364 | 0 | memprintf(err, "second argument must be a string (prefix) or omitted"); |
1365 | 0 | return 0; |
1366 | 0 | } |
1367 | | |
1368 | | /* Third argument (delimiter) is optional */ |
1369 | 0 | if (args[2].type != ARGT_STR && args[2].type != ARGT_STOP) { |
1370 | 0 | memprintf(err, "third argument must be a string (delimiter) or omitted"); |
1371 | 0 | return 0; |
1372 | 0 | } |
1373 | | |
1374 | 0 | return 1; |
1375 | 0 | } |
1376 | | |
1377 | | static int conv_check_var(struct arg *args, struct sample_conv *conv, |
1378 | | const char *file, int line, char **err_msg) |
1379 | 0 | { |
1380 | 0 | int cond_idx = 1; |
1381 | 0 | uint conditions = 0; |
1382 | 0 | int retval = vars_check_arg(&args[0], err_msg); |
1383 | |
|
1384 | 0 | while (retval && args[cond_idx].type == ARGT_STR) |
1385 | 0 | retval = vars_parse_cond_param(&args[cond_idx++].data.str, &conditions, err_msg); |
1386 | |
|
1387 | 0 | return retval; |
1388 | 0 | } |
1389 | | |
1390 | | /* |
1391 | | * Parses the "set-vars-from-map(<scope>,<map-file>[,<type>])" action. |
1392 | | * |
1393 | | * <scope> is one of sess/txn/req/res/check. <map-file> is a two-column map file |
1394 | | * whose first column holds unqualified variable names and whose second column |
1395 | | * holds their values. <type> is an optional value type among "str" (default), |
1396 | | * "int" and "bool"; it tells how to parse the values, which matters in |
1397 | | * particular for arithmetic on numeric variables. |
1398 | | * |
1399 | | * Returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR with <err> set on error. |
1400 | | */ |
1401 | | static enum act_parse_ret parse_set_vars_from_map(const char **args, int *arg, struct proxy *px, |
1402 | | struct act_rule *rule, char **err) |
1403 | 0 | { |
1404 | 0 | const char *kw = args[*arg - 1]; |
1405 | 0 | const char *p; |
1406 | 0 | int len; |
1407 | 0 | struct ist params, scope_ist, map_ist, type_ist; |
1408 | 0 | char *map_name = NULL; |
1409 | 0 | char scope_buf[6]; |
1410 | 0 | int scope; |
1411 | 0 | int type = SMP_T_STR; |
1412 | 0 | struct pat_ref *ref; |
1413 | 0 | const char *endptr = NULL; |
1414 | | |
1415 | | /* the whole "set-vars-from-map(...)" token is a single argument */ |
1416 | 0 | p = strchr(kw, '('); |
1417 | 0 | if (!p) { |
1418 | 0 | memprintf(err, "invalid or incomplete action '%s'. Expects 'set-vars-from-map(<scope>,<map-file>[,<type>])'", kw); |
1419 | 0 | return ACT_RET_PRS_ERR; |
1420 | 0 | } |
1421 | 0 | p++; /* jump the '(' */ |
1422 | 0 | len = strlen(p); |
1423 | 0 | if (len == 0 || p[len - 1] != ')') { |
1424 | 0 | memprintf(err, "incomplete argument after action '%s'. Expects 'set-vars-from-map(<scope>,<map-file>[,<type>])'", kw); |
1425 | 0 | return ACT_RET_PRS_ERR; |
1426 | 0 | } |
1427 | 0 | len--; /* drop the ')' */ |
1428 | | |
1429 | | /* split "<scope>,<map-file>[,<type>]" */ |
1430 | 0 | params = ist2(p, len); |
1431 | 0 | scope_ist = istsplit(¶ms, ','); |
1432 | 0 | map_ist = istsplit(¶ms, ','); |
1433 | 0 | type_ist = istsplit(¶ms, ','); |
1434 | |
|
1435 | 0 | if (!istlen(scope_ist) || !istlen(map_ist)) { |
1436 | 0 | memprintf(err, "action '%s' expects at least a scope and a map file name", kw); |
1437 | 0 | return ACT_RET_PRS_ERR; |
1438 | 0 | } |
1439 | | |
1440 | | /* Resolve the scope name. It is a slice of the whole action argument so |
1441 | | * it is copied into a local buffer to be nul-terminated before being |
1442 | | * parsed. |
1443 | | */ |
1444 | 0 | if (istlen(scope_ist) >= sizeof(scope_buf)) { |
1445 | 0 | memprintf(err, "invalid scope '%.*s' in action '%s'. Expects one of 'sess', 'txn', 'req', 'res' or 'check'", |
1446 | 0 | (int)istlen(scope_ist), istptr(scope_ist), kw); |
1447 | 0 | return ACT_RET_PRS_ERR; |
1448 | 0 | } |
1449 | 0 | memcpy(scope_buf, istptr(scope_ist), istlen(scope_ist)); |
1450 | 0 | scope_buf[istlen(scope_ist)] = '\0'; |
1451 | |
|
1452 | 0 | scope = var_parse_scope_str(scope_buf, &endptr); |
1453 | 0 | if (scope == -1 || scope == SCOPE_PROC || (endptr && *endptr)) { |
1454 | 0 | memprintf(err, "invalid scope '%.*s' in action '%s'. Expects one of 'sess', 'txn', 'req', 'res' or 'check'", |
1455 | 0 | (int)istlen(scope_ist), istptr(scope_ist), kw); |
1456 | 0 | return ACT_RET_PRS_ERR; |
1457 | 0 | } |
1458 | | |
1459 | | /* resolve the optional value type */ |
1460 | 0 | if (istlen(type_ist)) { |
1461 | 0 | if (isteq(type_ist, ist("str"))) |
1462 | 0 | type = SMP_T_STR; |
1463 | 0 | else if (isteq(type_ist, ist("int"))) |
1464 | 0 | type = SMP_T_SINT; |
1465 | 0 | else if (isteq(type_ist, ist("bool"))) |
1466 | 0 | type = SMP_T_BOOL; |
1467 | 0 | else { |
1468 | 0 | memprintf(err, "invalid type '%.*s' in action '%s'. Expects 'str', 'int' or 'bool'", |
1469 | 0 | (int)istlen(type_ist), istptr(type_ist), kw); |
1470 | 0 | return ACT_RET_PRS_ERR; |
1471 | 0 | } |
1472 | 0 | } |
1473 | | |
1474 | 0 | map_name = my_strndup(istptr(map_ist), istlen(map_ist)); |
1475 | 0 | if (!map_name) |
1476 | 0 | goto err; |
1477 | | |
1478 | | /* Load the map file as a two-column pattern reference, or reuse it if it |
1479 | | * was already loaded (e.g. by a map() converter or another such action). |
1480 | | */ |
1481 | 0 | ref = pat_ref_lookup(map_name); |
1482 | 0 | if (!ref) { |
1483 | 0 | chunk_printf(&trash, "map file '%s' loaded by set-vars-from-map at %s:%d", |
1484 | 0 | map_name, px->conf.args.file, px->conf.args.line); |
1485 | 0 | ref = pat_ref_new(map_name, trash.area, PAT_REF_MAP | PAT_REF_SMP); |
1486 | 0 | if (!ref) { |
1487 | 0 | memprintf(err, "out of memory while loading map file '%s'", map_name); |
1488 | 0 | goto err; |
1489 | 0 | } else if (!(ref->flags & PAT_REF_FILE)) { |
1490 | 0 | memprintf(err, "set-vars-from-map only accepts file-based maps ('%s')", map_name); |
1491 | 0 | goto err; |
1492 | 0 | } |
1493 | | |
1494 | 0 | if (!pat_ref_read_from_file_smp(ref, err)) |
1495 | 0 | goto err; |
1496 | 0 | } else if (!(ref->flags & PAT_REF_SMP)) { |
1497 | 0 | memprintf(err, "the file '%s' is used as a one-column file elsewhere and cannot be used " |
1498 | 0 | "by 'set-vars-from-map' which requires a two-column (key/value) file", map_name); |
1499 | 0 | goto err; |
1500 | 0 | } |
1501 | | |
1502 | 0 | rule->action = ACT_CUSTOM; |
1503 | 0 | rule->action_ptr = action_set_vars_from_map; |
1504 | 0 | rule->arg.act.p[0] = ref; |
1505 | 0 | rule->arg.act.p[1] = (void *)(uintptr_t)scope; |
1506 | 0 | rule->arg.act.p[2] = (void *)(uintptr_t)type; |
1507 | |
|
1508 | 0 | free(map_name); |
1509 | 0 | return ACT_RET_PRS_OK; |
1510 | | |
1511 | 0 | err: |
1512 | 0 | free(map_name); |
1513 | 0 | return ACT_RET_PRS_ERR; |
1514 | 0 | } |
1515 | | |
1516 | | /* This function is a common parser for using variables. It understands |
1517 | | * the format: |
1518 | | * |
1519 | | * set-var-fmt(<variable-name>[,<cond> ...]) <format-string> |
1520 | | * set-var(<variable-name>[,<cond> ...]) <expression> |
1521 | | * unset-var(<variable-name>) |
1522 | | * |
1523 | | * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error |
1524 | | * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr> |
1525 | | * is filled with the pointer to the expression to execute. The proxy is |
1526 | | * only used to retrieve the ->conf entries. |
1527 | | */ |
1528 | | static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px, |
1529 | | struct act_rule *rule, char **err) |
1530 | 0 | { |
1531 | 0 | const char *var_name = args[*arg-1]; |
1532 | 0 | int var_len; |
1533 | 0 | const char *kw_name; |
1534 | 0 | int flags = 0, set_var = 0; /* 0=unset-var, 1=set-var, 2=set-var-fmt */ |
1535 | 0 | struct sample empty_smp = { }; |
1536 | 0 | struct ist condition = IST_NULL; |
1537 | 0 | struct ist var = IST_NULL; |
1538 | 0 | struct ist varname_ist = IST_NULL; |
1539 | 0 | char *saved_name = NULL; |
1540 | |
|
1541 | 0 | if (strncmp(var_name, "set-var-fmt", 11) == 0) { |
1542 | 0 | var_name += 11; |
1543 | 0 | set_var = 2; |
1544 | 0 | } |
1545 | 0 | else if (strncmp(var_name, "set-var", 7) == 0) { |
1546 | 0 | var_name += 7; |
1547 | 0 | set_var = 1; |
1548 | 0 | } |
1549 | 0 | else if (strncmp(var_name, "unset-var", 9) == 0) { |
1550 | 0 | var_name += 9; |
1551 | 0 | set_var = 0; |
1552 | 0 | } |
1553 | |
|
1554 | 0 | if (*var_name != '(') { |
1555 | 0 | memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'", |
1556 | 0 | args[*arg-1]); |
1557 | 0 | return ACT_RET_PRS_ERR; |
1558 | 0 | } |
1559 | 0 | var_name++; /* jump the '(' */ |
1560 | 0 | var_len = strlen(var_name); |
1561 | 0 | var_len--; /* remove the ')' */ |
1562 | 0 | if (var_name[var_len] != ')') { |
1563 | 0 | memprintf(err, "incomplete argument after action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'", |
1564 | 0 | args[*arg-1]); |
1565 | 0 | return ACT_RET_PRS_ERR; |
1566 | 0 | } |
1567 | | |
1568 | | /* Parse the optional conditions. */ |
1569 | 0 | var = ist2(var_name, var_len); |
1570 | 0 | varname_ist = istsplit(&var, ','); |
1571 | 0 | var_len = istlen(varname_ist); |
1572 | |
|
1573 | 0 | condition = istsplit(&var, ','); |
1574 | |
|
1575 | 0 | if (istlen(condition) && set_var == 0) { |
1576 | 0 | memprintf(err, "unset-var does not expect parameters after the variable name. Only \"set-var\" and \"set-var-fmt\" manage conditions"); |
1577 | 0 | return ACT_RET_PRS_ERR; |
1578 | 0 | } |
1579 | | |
1580 | 0 | while (istlen(condition)) { |
1581 | 0 | struct buffer cond = {}; |
1582 | |
|
1583 | 0 | chunk_initlen(&cond, istptr(condition), 0, istlen(condition)); |
1584 | 0 | if (vars_parse_cond_param(&cond, &rule->arg.vars.conditions, err) == 0) |
1585 | 0 | return ACT_RET_PRS_ERR; |
1586 | | |
1587 | 0 | condition = istsplit(&var, ','); |
1588 | 0 | } |
1589 | | |
1590 | 0 | lf_expr_init(&rule->arg.vars.fmt); |
1591 | 0 | if (!vars_fill_desc(var_name, var_len, &rule->arg.vars.desc, err)) |
1592 | 0 | return ACT_RET_PRS_ERR; |
1593 | | |
1594 | | /* Save variable name for runtime use */ |
1595 | 0 | if (rule->arg.vars.desc.name && rule->arg.vars.desc.name_len > 0) { |
1596 | 0 | saved_name = my_strndup(rule->arg.vars.desc.name, rule->arg.vars.desc.name_len); |
1597 | 0 | if (!saved_name) { |
1598 | 0 | memprintf(err, "out of memory"); |
1599 | 0 | return ACT_RET_PRS_ERR; |
1600 | 0 | } |
1601 | 0 | rule->arg.vars.desc.name = saved_name; |
1602 | 0 | rule->arg.vars.desc.flags |= VDF_NAME_ALLOCATED; |
1603 | 0 | } |
1604 | | |
1605 | 0 | if (rule->arg.vars.desc.scope == SCOPE_PROC && |
1606 | 0 | !var_set(&rule->arg.vars.desc, &empty_smp, VF_CREATEONLY|VF_PERMANENT)) |
1607 | 0 | return ACT_RET_PRS_ERR; |
1608 | | |
1609 | | /* There is no fetch method when variable is unset. Just set the right |
1610 | | * action and return. */ |
1611 | 0 | if (!set_var) { |
1612 | 0 | rule->action = ACT_CUSTOM; |
1613 | 0 | rule->action_ptr = action_clear; |
1614 | 0 | rule->release_ptr = release_store_rule; |
1615 | 0 | return ACT_RET_PRS_OK; |
1616 | 0 | } |
1617 | | |
1618 | 0 | kw_name = args[*arg-1]; |
1619 | |
|
1620 | 0 | switch (rule->from) { |
1621 | 0 | case ACT_F_TCP_REQ_CON: |
1622 | 0 | flags = SMP_VAL_FE_CON_ACC; |
1623 | 0 | px->conf.args.ctx = ARGC_TCO; |
1624 | 0 | break; |
1625 | 0 | case ACT_F_TCP_REQ_SES: |
1626 | 0 | flags = SMP_VAL_FE_SES_ACC; |
1627 | 0 | px->conf.args.ctx = ARGC_TSE; |
1628 | 0 | break; |
1629 | 0 | case ACT_F_TCP_REQ_CNT: |
1630 | 0 | if (px->cap & PR_CAP_FE) |
1631 | 0 | flags |= SMP_VAL_FE_REQ_CNT; |
1632 | 0 | if (px->cap & PR_CAP_BE) |
1633 | 0 | flags |= SMP_VAL_BE_REQ_CNT; |
1634 | 0 | px->conf.args.ctx = ARGC_TRQ; |
1635 | 0 | break; |
1636 | 0 | case ACT_F_TCP_RES_CNT: |
1637 | 0 | if (px->cap & PR_CAP_FE) |
1638 | 0 | flags |= SMP_VAL_FE_RES_CNT; |
1639 | 0 | if (px->cap & PR_CAP_BE) |
1640 | 0 | flags |= SMP_VAL_BE_RES_CNT; |
1641 | 0 | px->conf.args.ctx = ARGC_TRS; |
1642 | 0 | break; |
1643 | 0 | case ACT_F_HTTP_REQ: |
1644 | 0 | if (px->cap & PR_CAP_FE) |
1645 | 0 | flags |= SMP_VAL_FE_HRQ_HDR; |
1646 | 0 | if (px->cap & PR_CAP_BE) |
1647 | 0 | flags |= SMP_VAL_BE_HRQ_HDR; |
1648 | 0 | px->conf.args.ctx = ARGC_HRQ; |
1649 | 0 | break; |
1650 | 0 | case ACT_F_HTTP_RES: |
1651 | 0 | if (px->cap & PR_CAP_FE) |
1652 | 0 | flags |= SMP_VAL_FE_HRS_HDR; |
1653 | 0 | if (px->cap & PR_CAP_BE) |
1654 | 0 | flags |= SMP_VAL_BE_HRS_HDR; |
1655 | 0 | px->conf.args.ctx = ARGC_HRS; |
1656 | 0 | break; |
1657 | 0 | case ACT_F_TCP_CHK: |
1658 | 0 | flags = SMP_VAL_BE_CHK_RUL; |
1659 | 0 | px->conf.args.ctx = ARGC_TCK; |
1660 | 0 | break; |
1661 | 0 | case ACT_F_CFG_PARSER: |
1662 | 0 | flags = SMP_VAL_CFG_PARSER; |
1663 | 0 | px->conf.args.ctx = ARGC_CFG; |
1664 | 0 | break; |
1665 | 0 | case ACT_F_CLI_PARSER: |
1666 | 0 | flags = SMP_VAL_CLI_PARSER; |
1667 | 0 | px->conf.args.ctx = ARGC_CLI; |
1668 | 0 | break; |
1669 | 0 | default: |
1670 | 0 | memprintf(err, |
1671 | 0 | "internal error, unexpected rule->from=%d, please report this bug!", |
1672 | 0 | rule->from); |
1673 | 0 | return ACT_RET_PRS_ERR; |
1674 | 0 | } |
1675 | | |
1676 | 0 | if (set_var == 2) { /* set-var-fmt */ |
1677 | 0 | if (!parse_logformat_string(args[*arg], px, &rule->arg.vars.fmt, 0, flags, err)) |
1678 | 0 | return ACT_RET_PRS_ERR; |
1679 | | |
1680 | 0 | (*arg)++; |
1681 | 0 | } else { |
1682 | | /* set-var */ |
1683 | 0 | rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file, |
1684 | 0 | px->conf.args.line, err, &px->conf.args, NULL); |
1685 | 0 | if (!rule->arg.vars.expr) |
1686 | 0 | return ACT_RET_PRS_ERR; |
1687 | | |
1688 | 0 | if (!(rule->arg.vars.expr->fetch->val & flags)) { |
1689 | 0 | memprintf(err, |
1690 | 0 | "fetch method '%s' extracts information from '%s', none of which is available here", |
1691 | 0 | kw_name, sample_src_names(rule->arg.vars.expr->fetch->use)); |
1692 | 0 | free(rule->arg.vars.expr); |
1693 | 0 | return ACT_RET_PRS_ERR; |
1694 | 0 | } |
1695 | 0 | } |
1696 | | |
1697 | 0 | rule->action = set_var; |
1698 | 0 | rule->action_ptr = action_store; |
1699 | 0 | rule->release_ptr = release_store_rule; |
1700 | 0 | return ACT_RET_PRS_OK; |
1701 | 0 | } |
1702 | | |
1703 | | |
1704 | | /* parses a global "set-var" directive. It will create a temporary rule and |
1705 | | * expression that are parsed, processed, and released on the fly so that we |
1706 | | * respect the real set-var syntax. These directives take the following format: |
1707 | | * set-var <name> <expression> |
1708 | | * set-var-fmt <name> <fmt> |
1709 | | * Note that parse_store() expects "set-var(name) <expression>" so we have to |
1710 | | * temporarily replace the keyword here. |
1711 | | */ |
1712 | | static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx, |
1713 | | const struct proxy *defpx, const char *file, int line, |
1714 | | char **err) |
1715 | 0 | { |
1716 | 0 | struct proxy px = { |
1717 | 0 | .id = "CFG", |
1718 | 0 | .conf.args = { .file = file, .line = line, }, |
1719 | 0 | .flags = PR_FL_CHECKED, |
1720 | 0 | }; |
1721 | 0 | struct act_rule rule = { |
1722 | 0 | .arg.vars.desc.scope = SCOPE_PROC, |
1723 | 0 | .from = ACT_F_CFG_PARSER, |
1724 | 0 | .conf = { .file = (char *)file, .line = line, }, |
1725 | 0 | }; |
1726 | 0 | enum obj_type objt = OBJ_TYPE_NONE; |
1727 | 0 | struct session *sess = NULL; |
1728 | 0 | enum act_parse_ret p_ret; |
1729 | 0 | char *old_arg1; |
1730 | 0 | char *tmp_arg1; |
1731 | 0 | int arg = 2; // variable name |
1732 | 0 | int ret = -1; |
1733 | 0 | int use_fmt = 0; |
1734 | |
|
1735 | 0 | LIST_INIT(&px.conf.args.list); |
1736 | |
|
1737 | 0 | use_fmt = strcmp(args[0], "set-var-fmt") == 0; |
1738 | |
|
1739 | 0 | if (!*args[1] || !*args[2]) { |
1740 | 0 | if (use_fmt) |
1741 | 0 | memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a format string.", args[0]); |
1742 | 0 | else |
1743 | 0 | memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]); |
1744 | 0 | goto end; |
1745 | 0 | } |
1746 | | |
1747 | 0 | tmp_arg1 = NULL; |
1748 | 0 | if (!memprintf(&tmp_arg1, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[1])) |
1749 | 0 | goto end; |
1750 | | |
1751 | | /* parse_store() will always return a message in <err> on error */ |
1752 | 0 | old_arg1 = args[1]; args[1] = tmp_arg1; |
1753 | 0 | p_ret = parse_store((const char **)args, &arg, &px, &rule, err); |
1754 | 0 | free(args[1]); args[1] = old_arg1; |
1755 | |
|
1756 | 0 | if (p_ret != ACT_RET_PRS_OK) |
1757 | 0 | goto end; |
1758 | | |
1759 | 0 | if (rule.arg.vars.desc.scope != SCOPE_PROC) { |
1760 | 0 | memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]); |
1761 | 0 | goto end; |
1762 | 0 | } |
1763 | | |
1764 | 0 | if (smp_resolve_args(&px, err) != 0) { |
1765 | 0 | release_sample_expr(rule.arg.vars.expr); |
1766 | 0 | indent_msg(err, 2); |
1767 | 0 | goto end; |
1768 | 0 | } |
1769 | | |
1770 | 0 | if (use_fmt && !(sess = session_new(&px, NULL, &objt))) { |
1771 | 0 | release_sample_expr(rule.arg.vars.expr); |
1772 | 0 | memprintf(err, "'%s': out of memory when trying to set variable '%s' in the global section.", args[0], args[1]); |
1773 | 0 | goto end; |
1774 | 0 | } |
1775 | | |
1776 | 0 | action_store(&rule, &px, sess, NULL, 0); |
1777 | 0 | release_sample_expr(rule.arg.vars.expr); |
1778 | 0 | if (sess) |
1779 | 0 | session_free(sess); |
1780 | |
|
1781 | 0 | ret = 0; |
1782 | 0 | end: |
1783 | 0 | return ret; |
1784 | 0 | } |
1785 | | |
1786 | | /* parse CLI's "get var <name>" */ |
1787 | | static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private) |
1788 | 0 | { |
1789 | 0 | struct vars *vars; |
1790 | 0 | struct var_desc desc; |
1791 | 0 | struct sample smp = { }; |
1792 | 0 | int i; |
1793 | |
|
1794 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_OPER)) |
1795 | 0 | return 1; |
1796 | | |
1797 | 0 | if (!*args[2]) |
1798 | 0 | return cli_err(appctx, "Missing process-wide variable identifier.\n"); |
1799 | | |
1800 | 0 | desc.name_hash = 0; |
1801 | 0 | desc.scope = SCOPE_PROC; |
1802 | 0 | desc.flags = 0; |
1803 | 0 | vars = get_vars(NULL, NULL, &desc); |
1804 | 0 | if (!vars || vars->scope != desc.scope) |
1805 | 0 | return 0; |
1806 | | |
1807 | 0 | if (!vars_get_by_name(args[2], strlen(args[2]), &smp, NULL)) |
1808 | 0 | return cli_err(appctx, "Variable not found.\n"); |
1809 | | |
1810 | | /* the sample returned by vars_get_by_name() is allocated into a trash |
1811 | | * chunk so we have no constraint to manipulate it. |
1812 | | */ |
1813 | 0 | chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]); |
1814 | |
|
1815 | 0 | if (!sample_casts[smp.data.type][SMP_T_STR] || |
1816 | 0 | !sample_casts[smp.data.type][SMP_T_STR](&smp)) { |
1817 | 0 | chunk_appendf(&trash, "(undisplayable)\n"); |
1818 | 0 | } else { |
1819 | | /* Display the displayable chars*. */ |
1820 | 0 | b_putchr(&trash, '<'); |
1821 | 0 | for (i = 0; i < smp.data.u.str.data; i++) { |
1822 | 0 | if (isprint((unsigned char)smp.data.u.str.area[i])) |
1823 | 0 | b_putchr(&trash, smp.data.u.str.area[i]); |
1824 | 0 | else |
1825 | 0 | b_putchr(&trash, '.'); |
1826 | 0 | } |
1827 | 0 | b_putchr(&trash, '>'); |
1828 | 0 | b_putchr(&trash, '\n'); |
1829 | 0 | b_putchr(&trash, 0); |
1830 | 0 | } |
1831 | 0 | return cli_msg(appctx, LOG_INFO, trash.area); |
1832 | 0 | } |
1833 | | |
1834 | | /* parse CLI's "set var <name>". It accepts: |
1835 | | * - set var <name> <expression> |
1836 | | * - set var <name> expr <expression> |
1837 | | * - set var <name> fmt <format> |
1838 | | */ |
1839 | | static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private) |
1840 | 0 | { |
1841 | 0 | struct proxy px = { |
1842 | 0 | .id = "CLI", |
1843 | 0 | .conf.args = { .file = "CLI", .line = 0, }, |
1844 | 0 | .flags = PR_FL_CHECKED, |
1845 | 0 | }; |
1846 | 0 | struct act_rule rule = { |
1847 | 0 | .arg.vars.desc.scope = SCOPE_PROC, |
1848 | 0 | .from = ACT_F_CLI_PARSER, |
1849 | 0 | .conf = { .file = "CLI", .line = 0, }, |
1850 | 0 | }; |
1851 | 0 | enum obj_type objt = OBJ_TYPE_NONE; |
1852 | 0 | struct session *sess = NULL; |
1853 | 0 | enum act_parse_ret p_ret; |
1854 | 0 | const char *tmp_args[3]; |
1855 | 0 | int tmp_arg; |
1856 | 0 | char *tmp_act; |
1857 | 0 | char *err = NULL; |
1858 | 0 | int nberr; |
1859 | 0 | int use_fmt = 0; |
1860 | |
|
1861 | 0 | LIST_INIT(&px.conf.args.list); |
1862 | |
|
1863 | 0 | if (!cli_has_level(appctx, ACCESS_LVL_OPER)) |
1864 | 0 | return 1; |
1865 | | |
1866 | 0 | if (!*args[2]) |
1867 | 0 | return cli_err(appctx, "Missing process-wide variable identifier.\n"); |
1868 | | |
1869 | 0 | if (!*args[3]) |
1870 | 0 | return cli_err(appctx, "Missing either 'expr', 'fmt' or expression.\n"); |
1871 | | |
1872 | 0 | if (*args[4]) { |
1873 | | /* this is the long format */ |
1874 | 0 | if (strcmp(args[3], "fmt") == 0) |
1875 | 0 | use_fmt = 1; |
1876 | 0 | else if (strcmp(args[3], "expr") != 0) { |
1877 | 0 | memprintf(&err, "'%s %s': arg type must be either 'expr' or 'fmt' but got '%s'.", args[0], args[1], args[3]); |
1878 | 0 | goto fail; |
1879 | 0 | } |
1880 | 0 | } |
1881 | | |
1882 | 0 | tmp_act = NULL; |
1883 | 0 | if (!memprintf(&tmp_act, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[2])) { |
1884 | 0 | memprintf(&err, "memory allocation error."); |
1885 | 0 | goto fail; |
1886 | 0 | } |
1887 | | |
1888 | | /* parse_store() will always return a message in <err> on error */ |
1889 | 0 | tmp_args[0] = tmp_act; |
1890 | 0 | tmp_args[1] = (*args[4]) ? args[4] : args[3]; |
1891 | 0 | tmp_args[2] = ""; |
1892 | 0 | tmp_arg = 1; // must point to the first arg after the action |
1893 | 0 | p_ret = parse_store(tmp_args, &tmp_arg, &px, &rule, &err); |
1894 | 0 | free(tmp_act); |
1895 | |
|
1896 | 0 | if (p_ret != ACT_RET_PRS_OK) |
1897 | 0 | goto fail; |
1898 | | |
1899 | 0 | if (rule.arg.vars.desc.scope != SCOPE_PROC) { |
1900 | 0 | memprintf(&err, "'%s %s': cannot set variable '%s', only scope 'proc' is permitted here.", args[0], args[1], args[2]); |
1901 | 0 | goto fail; |
1902 | 0 | } |
1903 | | |
1904 | 0 | err = NULL; |
1905 | 0 | nberr = smp_resolve_args(&px, &err); |
1906 | 0 | if (nberr) { |
1907 | 0 | release_sample_expr(rule.arg.vars.expr); |
1908 | 0 | indent_msg(&err, 2); |
1909 | 0 | goto fail; |
1910 | 0 | } |
1911 | | |
1912 | 0 | if (use_fmt && !(sess = session_new(&px, NULL, &objt))) { |
1913 | 0 | release_sample_expr(rule.arg.vars.expr); |
1914 | 0 | memprintf(&err, "memory allocation error."); |
1915 | 0 | goto fail; |
1916 | 0 | } |
1917 | | |
1918 | 0 | action_store(&rule, &px, sess, NULL, 0); |
1919 | 0 | release_sample_expr(rule.arg.vars.expr); |
1920 | 0 | if (sess) |
1921 | 0 | session_free(sess); |
1922 | |
|
1923 | 0 | appctx->st0 = CLI_ST_PROMPT; |
1924 | 0 | return 0; |
1925 | 0 | fail: |
1926 | 0 | return cli_dynerr(appctx, err); |
1927 | 0 | } |
1928 | | |
1929 | | static int vars_max_size(char **args, int section_type, struct proxy *curpx, |
1930 | | const struct proxy *defpx, const char *file, int line, |
1931 | | char **err, unsigned int *limit) |
1932 | 0 | { |
1933 | 0 | char *error; |
1934 | |
|
1935 | 0 | *limit = strtol(args[1], &error, 10); |
1936 | 0 | if (*error != 0) { |
1937 | 0 | memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]); |
1938 | 0 | return -1; |
1939 | 0 | } |
1940 | 0 | return 0; |
1941 | 0 | } |
1942 | | |
1943 | | static int vars_max_size_global(char **args, int section_type, struct proxy *curpx, |
1944 | | const struct proxy *defpx, const char *file, int line, |
1945 | | char **err) |
1946 | 0 | { |
1947 | 0 | return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit); |
1948 | 0 | } |
1949 | | |
1950 | | static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx, |
1951 | | const struct proxy *defpx, const char *file, int line, |
1952 | | char **err) |
1953 | 0 | { |
1954 | 0 | return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit); |
1955 | 0 | } |
1956 | | |
1957 | | static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx, |
1958 | | const struct proxy *defpx, const char *file, int line, |
1959 | | char **err) |
1960 | 0 | { |
1961 | 0 | return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit); |
1962 | 0 | } |
1963 | | |
1964 | | static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx, |
1965 | | const struct proxy *defpx, const char *file, int line, |
1966 | | char **err) |
1967 | 0 | { |
1968 | 0 | return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit); |
1969 | 0 | } |
1970 | | |
1971 | | static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx, |
1972 | | const struct proxy *defpx, const char *file, int line, |
1973 | | char **err) |
1974 | 0 | { |
1975 | 0 | return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit); |
1976 | 0 | } |
1977 | | |
1978 | | static int vars_max_size_check(char **args, int section_type, struct proxy *curpx, |
1979 | | const struct proxy *defpx, const char *file, int line, |
1980 | | char **err) |
1981 | 0 | { |
1982 | 0 | return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit); |
1983 | 0 | } |
1984 | | |
1985 | | static int vars_from_map_max(char **args, int section_type, struct proxy *curpx, |
1986 | | const struct proxy *defpx, const char *file, int line, |
1987 | | char **err) |
1988 | 0 | { |
1989 | 0 | return vars_max_size(args, section_type, curpx, defpx, file, line, err, &svfm_max); |
1990 | 0 | } |
1991 | | |
1992 | | /* early boot initialization */ |
1993 | | static void vars_init() |
1994 | 0 | { |
1995 | 0 | var_name_hash_seed = ha_random64(); |
1996 | | /* Initialize process vars */ |
1997 | 0 | vars_init_head(&proc_vars, SCOPE_PROC); |
1998 | 0 | } |
1999 | | |
2000 | | INITCALL0(STG_PREPARE, vars_init); |
2001 | | |
2002 | | static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { |
2003 | | |
2004 | | { "dump_all_vars", smp_fetch_dump_all_vars, ARG3(0,STR,STR,STR), smp_check_dump_all_vars, SMP_T_STR, SMP_USE_CONST }, |
2005 | | { "var", smp_fetch_var, ARG2(1,STR,STR), smp_check_var, SMP_T_ANY, SMP_USE_CONST }, |
2006 | | { /* END */ }, |
2007 | | }}; |
2008 | | |
2009 | | INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords); |
2010 | | |
2011 | | static struct sample_conv_kw_list sample_conv_kws = {ILH, { |
2012 | | { "set-var", smp_conv_store, ARG5(1,STR,STR,STR,STR,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY }, |
2013 | | { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY }, |
2014 | | { /* END */ }, |
2015 | | }}; |
2016 | | |
2017 | | INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws); |
2018 | | |
2019 | | static struct action_kw_list tcp_req_conn_kws = { { }, { |
2020 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2021 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2022 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2023 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2024 | | { /* END */ } |
2025 | | }}; |
2026 | | |
2027 | | INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_kws); |
2028 | | |
2029 | | static struct action_kw_list tcp_req_sess_kws = { { }, { |
2030 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2031 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2032 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2033 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2034 | | { /* END */ } |
2035 | | }}; |
2036 | | |
2037 | | INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws); |
2038 | | |
2039 | | static struct action_kw_list tcp_req_cont_kws = { { }, { |
2040 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2041 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2042 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2043 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2044 | | { /* END */ } |
2045 | | }}; |
2046 | | |
2047 | | INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws); |
2048 | | |
2049 | | static struct action_kw_list tcp_res_kws = { { }, { |
2050 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2051 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2052 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2053 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2054 | | { /* END */ } |
2055 | | }}; |
2056 | | |
2057 | | INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws); |
2058 | | |
2059 | | static struct action_kw_list tcp_check_kws = {ILH, { |
2060 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2061 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2062 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2063 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2064 | | { /* END */ } |
2065 | | }}; |
2066 | | |
2067 | | INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws); |
2068 | | |
2069 | | static struct action_kw_list http_req_kws = { { }, { |
2070 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2071 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2072 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2073 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2074 | | { /* END */ } |
2075 | | }}; |
2076 | | |
2077 | | INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws); |
2078 | | |
2079 | | static struct action_kw_list http_res_kws = { { }, { |
2080 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2081 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2082 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2083 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2084 | | { /* END */ } |
2085 | | }}; |
2086 | | |
2087 | | INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws); |
2088 | | |
2089 | | static struct action_kw_list http_after_res_kws = { { }, { |
2090 | | { "set-var-fmt", parse_store, KWF_MATCH_PREFIX }, |
2091 | | { "set-var", parse_store, KWF_MATCH_PREFIX }, |
2092 | | { "unset-var", parse_store, KWF_MATCH_PREFIX }, |
2093 | | { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX }, |
2094 | | { /* END */ } |
2095 | | }}; |
2096 | | |
2097 | | INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws); |
2098 | | |
2099 | | static struct cfg_kw_list cfg_kws = {{ },{ |
2100 | | { CFG_GLOBAL, "set-var", vars_parse_global_set_var }, |
2101 | | { CFG_GLOBAL, "set-var-fmt", vars_parse_global_set_var }, |
2102 | | { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global }, |
2103 | | { CFG_GLOBAL, "tune.vars.proc-max-size", vars_max_size_proc }, |
2104 | | { CFG_GLOBAL, "tune.vars.sess-max-size", vars_max_size_sess }, |
2105 | | { CFG_GLOBAL, "tune.vars.txn-max-size", vars_max_size_txn }, |
2106 | | { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres }, |
2107 | | { CFG_GLOBAL, "tune.vars.check-max-size", vars_max_size_check }, |
2108 | | { CFG_GLOBAL, "tune.vars.from-map.max", vars_from_map_max }, |
2109 | | { /* END */ } |
2110 | | }}; |
2111 | | |
2112 | | INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); |
2113 | | |
2114 | | |
2115 | | /* register cli keywords */ |
2116 | | static struct cli_kw_list cli_kws = {{ },{ |
2117 | | { { "get", "var", NULL }, "get var <name> : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL }, |
2118 | | { { "set", "var", NULL }, "set var <name> [fmt|expr] {<fmt>|<expr>}: set variable from an expression or a format", vars_parse_cli_set_var, NULL, NULL, NULL, ACCESS_EXPERIMENTAL }, |
2119 | | { { NULL }, NULL, NULL, NULL } |
2120 | | }}; |
2121 | | INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); |